March 8, 2010

This Code shows how to load in a 3 dimensional model from a stl file format which is often used for rapid prototyping and cad software. This example can load both text and binary stl files and was written to load the model files from the reprap open source 3d printer project.

```import osimport structfrom OpenGL.GL import *from OpenGL.GLU import *import pygamefrom pygame.locals import *#class for a 3d pointclass createpoint:    def __init__(self,p,c=(1,0,0)):        self.point_size=0.5        self.color=c        self.x=p[0]        self.y=p[1]        self.z=p[2]
def glvertex(self):        glVertex3f(self.x,self.y,self.z)#class for a 3d face on a modelclass createtriangle:    points=None    normal=None    def __init__(self,p1,p2,p3,n=None):        #3 points of the triangle        self.points=createpoint(p1),createpoint(p2),createpoint(p3)
#triangles normal        self.normal=createpoint(self.calculate_normal(self.points[0],self.points[1],self.points[2]))#(0,1,0)#
#calculate vector / edge    def calculate_vector(self,p1,p2):        return -p1.x+p2.x,-p1.y+p2.y,-p1.z+p2.z
def calculate_normal(self,p1,p2,p3):        a=self.calculate_vector(p3,p2)        b=self.calculate_vector(p3,p1)        #calculate the cross product returns a vector        return self.cross_product(a,b)

def cross_product(self,p1,p2):        return (p1[1]*p2[2]-p2[1]*p1[2]) , (p1[2]*p2[0])-(p2[2]*p1[0]) , (p1[0]*p2[1])-(p2[0]*p1[1])class loader:    model=[]
#return the faces of the triangles    def get_triangles(self):        if self.model:            for face in self.model:                yield face    #draw the models faces    def draw(self):        glBegin(GL_TRIANGLES)        for tri in self.get_triangles():            glNormal3f(tri.normal.x,tri.normal.y,tri.normal.z)            glVertex3f(tri.points[0].x,tri.points[0].y,tri.points[0].z)            glVertex3f(tri.points[1].x,tri.points[1].y,tri.points[1].z)            glVertex3f(tri.points[2].x,tri.points[2].y,tri.points[2].z)        glEnd()
#load stl file detects if the file is a text file or binary file    def load_stl(self,filename):        #read start of file to determine if its a binay stl file or a ascii stl file        fp=open(filename,'rb')        h=fp.read(80)        type=h[0:5]        fp.close()        if type=='solid':            print "reading text file"+str(filename)            self.load_text_stl(filename)        else:            print "reading binary stl file "+str(filename,)            self.load_binary_stl(filename)
#read text stl match keywords to grab the points to build the model    def load_text_stl(self,filename):        fp=open(filename,'r')        for line in fp.readlines():            words=line.split()            if len(words)>0:                if words[0]=='solid':                    self.name=words[1]                if words[0]=='facet':                    center=[0.0,0.0,0.0]                    triangle=[]                    normal=(eval(words[2]),eval(words[3]),eval(words[4]))
if words[0]=='vertex':                    triangle.append((eval(words[1]),eval(words[2]),eval(words[3])))

if words[0]=='endloop':                    #make sure we got the correct number of values before storing                    if len(triangle)==3:                        self.model.append(createtriangle(triangle[0],triangle[1],triangle[2],normal))        fp.close()    #load binary stl file check wikipedia for the binary layout of the file    #we use the struct library to read in and convert binary data into a format we can use    def load_binary_stl(self,filename):        fp=open(filename,'rb')        h=fp.read(80)        l=struct.unpack('I',fp.read(4))[0]        count=0        while True:            try:                p=fp.read(12)                if len(p)==12:                    n=struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]
glDepthFunc(GL_LEQUAL)        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glEnable(GL_COLOR_MATERIAL)        glEnable(GL_LIGHTING)        glEnable(GL_LIGHT0)
glLight(GL_LIGHT0, GL_POSITION,  (0, 1, 1, 0))
glMatrixMode(GL_MODELVIEW)
def resize(self,(width, height)):        if height==0:            height=1        glViewport(0, 0, width, height)        glMatrixMode(GL_PROJECTION)        glLoadIdentity()        gluPerspective(45, 1.0*width/height, 0.1, 100.0)        #gluLookAt(0.0,0.0,45.0,0,0,0,0,40.0,0)        glMatrixMode(GL_MODELVIEW)        glLoadIdentity()    def init(self):        glShadeModel(GL_SMOOTH)        glClearColor(0.0, 0.0, 0.0, 0.0)        glClearDepth(1.0)        glEnable(GL_DEPTH_TEST)        glShadeModel(GL_SMOOTH)
glDepthFunc(GL_LEQUAL)        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHTING)        glEnable(GL_LIGHT0)
glLight(GL_LIGHT0, GL_POSITION,  (0, 1, 1, 0))