PyOpenGLでえせPLYファイルビューワ
PyOpenGLでPLYファイルのてきとうなパーサを書いてスタンフォードバニーを表示させた。
超がつくほどのてきとうな仕様のパーサで色々と決め打ち。法線は描画時に面法線を計算して設定する。・・・いまのプログラムだと法線の方向が逆になる場合がある気がするけどいいや。
以下を参考にして、
http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
以下のurlから参照できるplyファイルで動作を確認した。
http://www.cs.virginia.edu/~gfx/Courses/2001/Advanced.spring.01/plymodels/
やっぱりビジュアルなプログラムは達成感があっていいなー。
# coding: utf-8 # x,y,zの順で頂点がならんでいるのが前提 # 面は3点からなるのが前提 from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * import sys class PlyModel(object): def __init__(self): self.vertices = [] # [(x,y,z)] self.faces = [] # [(xi,yi,zi)] self.nvertex = None self.nface = None self.maxx = -1e10 self.maxy = -1e10 self.maxz = -1e10 def load(self, filepath): global cx,cy,cz f = open(filepath, "rb") # read header while True: lst = f.readline().split() if lst[0] == "end_header": break if lst[0] == "element": if lst[1] == "vertex": self.nvertex = int(lst[2]) if lst[1] == "face": self.nface = int(lst[2]) # read vertex for i in xrange(self.nvertex): v = map(float, f.readline().split()[:3]) self.maxx = max(self.maxx, v[0]) self.maxy = max(self.maxy, v[1]) self.maxz = max(self.maxz, v[2]) self.vertices.append(v) # read face for i in xrange(self.nface): x = map(int, f.readline().split()[1:]) self.faces.append(x) f.close() def draw(self): # 面法線を設定して描画する glBegin(GL_TRIANGLES) for index in self.faces: p1,p2,p3 = map(lambda i: self.vertices[i], index) u = map(lambda x,y: x-y, p2, p1) v = map(lambda x,y: x-y, p3, p1) cross = ( # 法線の計算 u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0], ) # 正規化 norm = sum(map(lambda x:x*x, cross))**.5 cross = map(lambda x:x/norm, cross) glNormal3fv(cross) glVertex3fv(p1) glVertex3fv(p2) glVertex3fv(p3) glEnd() cx,cy,cz=100,100,100 sx,sy,sz=0,0,0 displist=None def display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(60, float(glutGet(GLUT_WINDOW_WIDTH))/glutGet(GLUT_WINDOW_HEIGHT), .1, 1e8) gluLookAt(cx,cy,cz, sx,sy,sz, 0,1,0) glMatrixMode(GL_MODELVIEW) glCallList(displist) glutSwapBuffers() def reshape(w, h): glViewport(0, 0, w, h) glutPostRedisplay() def keyboard(k, x, y): global cx,cy,cz,sx,sy,sz if k in "qQ\33": sys.exit() if k == 'u': cx += 1 if k == 'j': cx -= 1 if k == 'i': cy += 1 if k == 'k': cy -= 1 if k == 'o': cz += 1 if k == 'l': cz -= 1 if k == 'p': sx += 1 if k == ';': sx -= 1 if k == '@': sy += 1 if k == ':': sy -= 1 if k == '[': sz += 1 if k == ']': sz -= 1 glutPostRedisplay() def init(): global displist,cx,cy,cz glClearColor(1,1,1,1) glEnable(GL_DEPTH_TEST) glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glLightfv(GL_LIGHT0, GL_POSITION, [200,300,105,0]) glLightfv(GL_LIGHT0, GL_AMBIENT, [.1,.1,.1,1]) ply = PlyModel() ply.load(sys.argv[1]) displist = glGenLists(1) glNewList(displist, GL_COMPILE) ply.draw() glEndList() cx,cy,cz = ply.maxx,ply.maxy,ply.maxz def main(): glutInit(sys.argv) glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutCreateWindow("ply file view") init() glutDisplayFunc(display) glutReshapeFunc(reshape) glutKeyboardFunc(keyboard) glutMainLoop() if __name__ == '__main__': main()