python 读取3D obj文件

给自己的写的
虽然感觉自己cmake文件的编写已经掌握的差不多了,各种库的调用也写了相应的文件,用时直接include就行。但是相比于python,c++还是复杂的多,于是这次决心好好学学python的opengl,这样调试也方便些。

@19-4-17
我将整理好的代码放到了github上,目前还是推荐对c++熟的话尽可能用c++。

本博客介绍python读取3D obj文件并进行显示与控制。所用的python库主要有 pygame, pyopengl。这个pygame让我相见恨晚啊(好像很多强化学习的示例程序也用它)。

作者已经在 win7 64bit, py3.6的机器上配置成功,可以加载带有mtl的obj文件。

也不介绍了,其实和c++的也一样,这里直接贴程序了,分成三份。

main.py 包含了一个 pickle 功能,主要是希望第一次读取后后面能读的快些。(偷懒了这里,需要手动修改 if 1==1)

加载模型的时候,由于不知道模型的位置,我仿照c++中trimesh的思想,为obj物体制作了 bbox。所以下面这两个变换要先做

s = [ 10/obj.bbox_half_r ]*3
glScale(*s)

t = -obj.bbox_center
glTranslate(*t)

具体看程序就一目了然了。 注意在opengl中先做的变换,写程序的时候要放在最后面。(这里只是简单提示下,懂了原理这个自己明白,不要介意我说什么)

objloader.py 用来加载模型和纹理

import numpy as np
import pygame, OpenGL

'''
the original is here https://www.pygame.org/wiki/OBJFileLoader
@2018-1-2 author chj
change for easy use
'''

def MTL(fdir,filename):
    contents = {}
    mtl = None
    for line in open(fdir+filename, "r"):
        if line.startswith('#'): continue
        values = line.split()
        if not values: continue
        if values[0] == 'newmtl':
            mtl = contents[values[1]] = {}
        elif mtl is None:
            raise ValueError( "mtl file doesn't start with newmtl stmt" )
        elif values[0] == 'map_Kd':
            # load the texture referred to by this declaration
            mtl[values[0]] = values[1]
            surf = pygame.image.load(fdir+mtl['map_Kd'])
            image = pygame.image.tostring(surf, 'RGBA', 1)
            ix, iy = surf.get_rect().size
            texid = mtl['texture_Kd'] = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, texid)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                GL_LINEAR)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA,
                GL_UNSIGNED_BYTE, image)
        else:
            #mtl[values[0]] = map(float, values[1:])
            
            mtl[values[0]] = [ float(x) for x in values[1:4]]
    return contents
 
class OBJ:
    def __init__(self, fdir, filename, swapyz=False):
        """Loads a Wavefront OBJ file. """
        self.vertices = []
        self.normals = []
        self.texcoords = []
        self.faces = []

        self.mtl=None

        material = None
        for line in open(fdir+filename, "r"):
            if line.startswith('#'): continue
            values = line.split()
            if not values: continue
            if values[0] == 'v':
                #v = map(float, values[1:4])
                v=[ float(x) for x in values[1:4]]
                if swapyz:
                    v = v[0], v[2], v[1]
                self.vertices.append(v)
            elif values[0] == 'vn':
                #v = map(float, values[1:4])
                v=[ float(x) for x in values[1:4]]
                if swapyz:
                    v = v[0], v[2], v[1]
                self.normals.append(v)
            elif values[0] == 'vt':
                v = [float(x) for x in values[1:3]]

                self.texcoords.append(v)
            elif values[0] in ('usemtl', 'usemat'):
                material = values[1]
            elif values[0] == 'mtllib':
                #print(values[1])
                #self.mtl = MTL(fdir,values[1])
                self.mtl = [fdir,values[1]]
            elif values[0] == 'f':
                face = []
                texcoords = []
                norms = []
                for v in values[1:]:
                    w = v.split('/')
                    face.append(int(w[0]))
                    if len(w) >= 2 and len(w[1]) > 0:
                        texcoords.append(int(w[1]))
                    else:
                        texcoords.append(0)
                    if len(w) >= 3 and len(w[2]) > 0:
                        norms.append(int(w[2]))
                    else:
                        norms.append(0)
                self.faces.append((face, norms, texcoords, material))

    def create_bbox(self):
        # self.vertices is not None
        ps=np.array(self.vertices)
        vmin=ps.min(axis=0)
        vmax=ps.max(axis=0)

        self.bbox_center=(vmax+vmin)/2
        self.bbox_half_r=np.max(vmax-vmin)/2


    def create_gl_list(self):
        if self.mtl is not None:
            self.mtl = MTL( *self.mtl )

        self.gl_list = glGenLists(1)
        glNewList(self.gl_list, GL_COMPILE)
        glEnable(GL_TEXTURE_2D)
        glFrontFace(GL_CCW)
        #glCullFace(GL_BACK)
        #glEnable(GL_CULL_FACE)

        for face in self.faces:
            vertices, normals, texture_coords, material = face
 
            mtl = self.mtl[material]
            if 'texture_Kd' in mtl:
                # use diffuse texmap
                glBindTexture(GL_TEXTURE_2D, mtl['texture_Kd'])
            else:
                # just use diffuse colour
                #print(mtl['Kd'],"----")
                glColor(*mtl['Kd'])
 
            glBegin(GL_POLYGON)
            for i in range(len(vertices)):
                if normals[i] > 0:
                    glNormal3fv(self.normals[normals[i] - 1])
                if texture_coords[i] > 0:
                    glTexCoord2fv(self.texcoords[texture_coords[i] - 1])
                glVertex3fv(self.vertices[vertices[i] - 1])
            glEnd()
        glDisable(GL_TEXTURE_2D)
        glEndList()

light.py 主要用来设置光照和相机

import numpy as np
import pygame, OpenGL
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GLUT.freeglut import *

def setup_lighting():
    draw_2side=False
    c=[1.0,1.0,1.0]

    glColor3fv(c)

    mat_specular=[0.18, 0.18, 0.18, 0.18 ]

    mat_shininess=[ 64 ]
    global_ambient=[ 0.3, 0.3, 0.3, 0.05 ]
    light0_ambient=[ 0, 0, 0, 0 ]
    light0_diffuse=[ 0.85, 0.85, 0.8, 0.85 ]

    light1_diffuse=[-0.01, -0.01, -0.03, -0.03 ]
    light0_specular=[ 0.85, 0.85, 0.85, 0.85 ]
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, draw_2side);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);


class camera():
    class Ortho:
        # left, right, bottom, top, near, far
        params=np.array([-1, 1, -1, 1, 1, -1], np.float32)
        bbox=params[0:4]
        nf=params[4:] # near far

main.py 主要的显示程序

# 注意把那两文件加进来,可以
# import light
# from objloader import OBJ

def step1():
    fn = "cite/obj.pkl"
    if 1 == 1:
        obj = OBJ(fobjdir, fobj, swapyz=True)
        obj.create_bbox()

        with open(fn, 'wb') as f:  # open file with write-mode
            pickle.dump(obj, f)  #picklestring = pickle.dumps(summer)
    else:
        with open(fn, 'rb') as f:
            obj = pickle.load(f)

    pygame.init()
    # 设置成一样的 这样 glOrtho 就简单些
    viewport = (600, 600)
    srf = pygame.display.set_mode(viewport, OPENGL | DOUBLEBUF)

    light.setup_lighting()
    glLightfv(GL_LIGHT0, GL_POSITION, (0, 0, -100, 0.0)) # 指的是光的朝向

    glEnable(GL_DEPTH_TEST)
    glShadeModel(GL_SMOOTH)  # most obj files expect to be smooth-shaded

    obj.create_gl_list()

    clock = pygame.time.Clock()

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    #gluPerspective(60.0, width / float(height), 1, 100.0)
    cam=light.camera
    #cam.Ortho.params=cam.Ortho.params*15
    cam.Ortho.bbox[:] = cam.Ortho.bbox * 13
    cam.Ortho.nf[:] = cam.Ortho.nf * 20
    glOrtho(*cam.Ortho.params)

    glEnable(GL_DEPTH_TEST)
    glMatrixMode(GL_MODELVIEW)

    rx, ry = (0, 0)
    tx, ty = (0, 0)
    zpos = 5
    rotate = move = False
    while 1:
        clock.tick(30)
        for e in pygame.event.get():
            if e.type == QUIT:
                sys.exit()
            elif e.type == KEYDOWN and e.key == K_ESCAPE:
                sys.exit()
            elif e.type == MOUSEBUTTONDOWN:
                if e.button == 4:
                    zpos = max(1, zpos - 1)
                elif e.button == 5:
                    zpos += 1
                elif e.button == 1:
                    rotate = True
                elif e.button == 3:
                    move = True
            elif e.type == MOUSEBUTTONUP:
                if e.button == 1:
                    rotate = False
                elif e.button == 3:
                    move = False
            elif e.type == MOUSEMOTION:
                #p(e.rel)
                i, j = e.rel
                if rotate:
                    rx -= i
                    ry -= j
                if move:
                    tx += i
                    ty -= j

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()


        # RENDER OBJECT
        glTranslate(tx / 20., ty / 20., - zpos)
        glRotate(ry/5, 1, 0, 0)
        glRotate(rx/5, 0, 0, 1)

        s = [ 10/obj.bbox_half_r ]*3
        glScale(*s)

        t = -obj.bbox_center
        glTranslate(*t)

        glCallList(obj.gl_list)

        pygame.display.flip()
### 回答1: Python可以使用许多不同的库来导入现成的常用三维模型格式,其中包括OpenGL、OpenSceneGraph、OBJ、COLLADA和其它许多格式。可以利用Python中的一些包来轻松完成这些操作,例如NumPy和PyOpenGL。 ### 回答2: 在Python中,可以使用一些现成的库来导入和分析常用的三维模型格式。 首先,可以使用`numpy`库来处理数值计算和数组操作。`numpy`提供了一些功能强大的函数和方法,可以用来处理三维模型的数据。 其次,可以使用`open3d`库来导入和处理三维模型。`open3d`是一个广泛使用的开源库,支持多种三维模型格式,如PLY、OBJ、STL等。使用`open3d`可以读取三维模型的顶点、法线、面片等信息,并进行各种操作和分析。 下面是一个简单的示例代码,演示了如何使用`open3d`导入和分析一个PLY格式的三维模型: ```python import open3d as o3d # 加载PLY格式的模型文件 mesh = o3d.io.read_triangle_mesh("example.ply") # 打印模型信息 print("顶点数量:", len(mesh.vertices)) print("面片数量:", len(mesh.triangles)) # 可以进行进一步的分析和处理,如计算法线、体积、表面积等 # 保存处理后的模型 o3d.io.write_triangle_mesh("output.ply", mesh) ``` 以上代码使用`o3d.io.read_triangle_mesh`函数导入模型,然后可以通过`mesh.vertices`和`mesh.triangles`来访问顶点和面片的信息。通过`o3d.io.write_triangle_mesh`函数可以将处理后的模型保存到文件中。 除了`open3d`,还有其他一些库和工具可用于导入和分析三维模型,如`PyMCubes`、`trimesh`、`meshlab`等。根据实际需求和使用场景,可以选择适合的工具来处理三维模型数据。 ### 回答3: 在Python中,可以使用一些现成的库来导入和分析常见的三维模型格式。以下是一种常见的导入和分析三维模型的流程: 1. 安装必要的库:首先需要安装一些Python库,如NumPy、PyMesh、trimesh等。这些库提供了高效的三维模型处理功能。 2. 导入模型文件:使用相应的库,例如使用trimesh库的load函数来导入三维模型文件。常见的三维模型文件格式包括obj、stl、ply等。 3. 数据操作和分析:一旦模型文件被成功导入,可以对导入的模型进行各种操作和分析。例如,可以计算模型的表面积、体积、中心点等属性。可以使用NumPy库进行数学运算和向量操作。 4. 可视化:可以使用库中的可视化功能来查看和展示导入的三维模型。例如,使用PyMesh库的plot函数可以快速绘制模型的外观。 5. 进一步分析:在导入和可视化模型之后,可以进一步进行分析。例如,可以计算模型的法线、曲率等几何属性。可以进行形状匹配、曲面重建等操作。 总之,Python通过使用现成的库,可以方便地导入、操作和分析常见的三维模型文件格式。这样可以快速进行各种三维模型相关的计算和可视化分析。
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值