#Contains a basic class for viewing a mesh, wrapping around glcanvas. Includes #Basic functionality for setting up lighting/cameras and handling mouse events #for changing viewing parameters from OpenGL.GL import * from OpenGL.arrays import vbo import wx from wx import glcanvas from Primitives3D import * from PolyMesh import * from LaplacianMesh import * from Cameras3D import * DEFAULT_SIZE = wx.Size(1200, 800) DEFAULT_POS = wx.Point(10, 10) def saveImageGL(mvcanvas, filename): view = glGetIntegerv(GL_VIEWPORT) img = wx.EmptyImage(view[2], view[3] ) pixels = glReadPixels(0, 0, view[2], view[3], GL_RGB, GL_UNSIGNED_BYTE) img.SetData( pixels ) img = img.Mirror(False) img.SaveFile(filename, wx.BITMAP_TYPE_PNG) def saveImage(canvas, filename): s = wx.ScreenDC() w, h = canvas.size.Get() b = wx.EmptyBitmap(w, h) m = wx.MemoryDCFromDC(s) m.SelectObject(b) m.Blit(0, 0, w, h, s, 70, 0) m.SelectObject(wx.NullBitmap) b.SaveFile(filename, wx.BITMAP_TYPE_PNG) class BasicMeshCanvas(glcanvas.GLCanvas): def __init__(self, parent): attribs = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) glcanvas.GLCanvas.__init__(self, parent, -1, attribList = attribs) self.context = glcanvas.GLContext(self) self.parent = parent #Camera state variables self.size = self.GetClientSize() #self.camera = MouseSphericalCamera(self.size.x, self.size.y) self.camera = MousePolarCamera(self.size.width, self.size.height) #Main state variables self.MousePos = [0, 0] self.bbox = BBox3D() #Face mesh variables and manipulation variables self.mesh = None self.meshCentroid = None self.displayMeshFaces = True self.displayMeshEdges = False self.displayMeshVertices = True self.displayVertexNormals = False self.displayFaceNormals = False self.useLighting = True self.useTexture = False self.GLinitialized = False #GL-related events wx.EVT_ERASE_BACKGROUND(self, self.processEraseBackgroundEvent) wx.EVT_SIZE(self, self.processSizeEvent) wx.EVT_PAINT(self, self.processPaintEvent) #Mouse Events wx.EVT_LEFT_DOWN(self, self.MouseDown) wx.EVT_LEFT_UP(self, self.MouseUp) wx.EVT_RIGHT_DOWN(self, self.MouseDown) wx.EVT_RIGHT_UP(self, self.MouseUp) wx.EVT_MIDDLE_DOWN(self, self.MouseDown) wx.EVT_MIDDLE_UP(self, self.MouseUp) wx.EVT_MOTION(self, self.MouseMotion) def initMeshBBox(self): if self.mesh: self.bbox = self.mesh.getBBox() print "Mesh BBox: %s\n"%self.bbox self.camera.centerOnBBox(self.bbox, theta = -math.pi/2, phi = math.pi/2) def viewFromFront(self, evt): self.camera.centerOnBBox(self.bbox, theta = -math.pi/2, phi = math.pi/2) self.Refresh() def viewFromTop(self, evt): self.camera.centerOnBBox(self.bbox, theta = -math.pi/2, phi = 0) self.Refresh() def viewFromSide(self, evt): self.camera.centerOnBBox(self.bbox, theta = -math.pi, phi = math.pi/2) self.Refresh() def processEraseBackgroundEvent(self, event): pass #avoid flashing on MSW. def processSizeEvent(self, event): self.size = self.GetClientSize() glViewport(0, 0, self.size.width, self.size.height) #Update camera parameters based on new size self.camera = MousePolarCamera(self.size.width, self.size.height) self.camera.centerOnBBox(self.bbox, math.pi/2, math.pi/2) def processPaintEvent(self, event): dc = wx.PaintDC(self) self.SetCurrent(self.context) if not self.GLinitialized: self.initGL() self.GLinitialized = True self.repaint() def drawMeshStandard(self): glEnable(GL_LIGHTING) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [0.2, 0.2, 0.2, 1.0]) glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 64) #Set up modelview matrix self.camera.gotoCameraFrame() glLightfv(GL_LIGHT0, GL_POSITION, np.array([0, 0, 0, 1])) self.mesh.renderGL(self.displayMeshEdges, self.displayMeshVertices, self.displayMeshFaces, self.displayVertexNormals, self.displayFaceNormals, self.useLighting, self.useTexture) def setupPerspectiveMatrix(self, nearDist = -1, farDist = -1): glMatrixMode(GL_PROJECTION) glLoadIdentity() if nearDist == -1: farDist = self.camera.eye - self.bbox.getCenter() farDist = np.sqrt(farDist.dot(farDist)) + self.bbox.getDiagLength() nearDist = farDist/500.0 gluPerspective(180.0*self.camera.yfov/M_PI, float(self.size.x)/self.size.y, nearDist, farDist) def repaint(self): self.setupPerspectiveMatrix() glClearColor(0.0, 0.0, 0.0, 0.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) if self.mesh: self.drawMeshStandard() self.SwapBuffers() def initGL(self): glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [0.2, 0.2, 0.2, 1.0]) glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE) glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1.0]) glEnable(GL_LIGHT0) glLightfv(GL_LIGHT1, GL_DIFFUSE, [0.5, 0.5, 0.5, 1.0]) glEnable(GL_LIGHT1) glEnable(GL_NORMALIZE) glEnable(GL_LIGHTING) glEnable(GL_DEPTH_TEST) def handleMouseStuff(self, x, y): #Invert y from what the window manager says y = self.size.height - y self.MousePos = [x, y] def MouseDown(self, evt): state = wx.GetMouseState() x, y = evt.GetPosition() self.CaptureMouse() self.handleMouseStuff(x, y) self.Refresh() def MouseUp(self, evt): x, y = evt.GetPosition() self.handleMouseStuff(x, y) self.ReleaseMouse() self.Refresh() def MouseMotion(self, evt): state = wx.GetMouseState() x, y = evt.GetPosition() [lastX, lastY] = self.MousePos self.handleMouseStuff(x, y) dX = self.MousePos[0] - lastX dY = self.MousePos[1] - lastY if evt.Dragging(): #Translate/rotate shape if evt.MiddleIsDown(): self.camera.translate(dX, dY) elif evt.RightIsDown(): self.camera.zoom(-dY)#Want to zoom in as the mouse goes up elif evt.LeftIsDown(): self.camera.orbitLeftRight(dX) self.camera.orbitUpDown(dY) self.Refresh() if __name__ == '__main__': app = wx.PySimpleApp() frame = wx.Frame(None, wx.ID_ANY, "Basic Mesh Canvas", DEFAULT_POS, DEFAULT_SIZE) g = BasicMeshCanvas(frame) g.mesh = getDodecahedronMesh() g.initMeshBBox() frame.canvas = g frame.Show() app.MainLoop() app.Destroy()