diff --git a/demo/GLCanvas.py b/demo/GLCanvas.py index 7c158b28..d236f01b 100644 --- a/demo/GLCanvas.py +++ b/demo/GLCanvas.py @@ -1,7 +1,10 @@ #!/usr/bin/env python -import wx +import os import sys +from math import pi, sin, cos + +import wx try: from wx import glcanvas @@ -13,11 +16,12 @@ try: # The Python OpenGL package can be found at # http://PyOpenGL.sourceforge.net/ from OpenGL.GL import * - from OpenGL.GLUT import * + from OpenGL.GLU import * haveOpenGL = True except ImportError: haveOpenGL = False + #---------------------------------------------------------------------- @@ -28,7 +32,7 @@ buttonDefs = { class ButtonPanel(wx.Panel): def __init__(self, parent, log): - wx.Panel.__init__(self, parent, -1) + wx.Panel.__init__(self, parent, wx.ID_ANY) self.log = log box = wx.BoxSizer(wx.VERTICAL) @@ -37,14 +41,14 @@ class ButtonPanel(wx.Panel): for k in keys: text = buttonDefs[k][1] btn = wx.Button(self, k, text) - box.Add(btn, 0, wx.ALIGN_CENTER|wx.ALL, 15) + box.Add(btn, 0, wx.ALIGN_CENTER | wx.ALL, 15) self.Bind(wx.EVT_BUTTON, self.OnButton, btn) - #** Enable this to show putting a GLCanvas on the wx.Panel + #** Enable this to show putting a GLCanvas on the wx.Panel . if 0: c = CubeCanvas(self) c.SetSize((200, 200)) - box.Add(c, 0, wx.ALIGN_CENTER|wx.ALL, 15) + box.Add(c, 0, wx.ALIGN_CENTER | wx.ALL, 15) self.SetAutoLayout(True) self.SetSizer(box) @@ -61,7 +65,8 @@ class ButtonPanel(wx.Panel): elif not haveOpenGL: dlg = wx.MessageDialog(self, 'The OpenGL package was not found. You can get it at\n' - 'http://PyOpenGL.sourceforge.net/', + 'http://PyOpenGL.sourceforge.net/ \n' + 'or $ pip install PyOpenGL PyOpenGL_accelerate', 'Sorry', wx.OK | wx.ICON_WARNING) dlg.ShowModal() dlg.Destroy() @@ -69,7 +74,7 @@ class ButtonPanel(wx.Panel): else: canvasClassName = buttonDefs[evt.GetId()][0] canvasClass = eval(canvasClassName) - frame = wx.Frame(None, -1, canvasClassName, size=(400,400)) + frame = wx.Frame(None, wx.ID_ANY, canvasClassName, size=(400, 400)) canvas = canvasClass(frame) frame.Show(True) @@ -80,7 +85,7 @@ class MyCanvasBase(glcanvas.GLCanvas): self.init = False self.context = glcanvas.GLContext(self) - # initial mouse position + # Initial mouse position. self.lastx = self.x = 30 self.lasty = self.y = 30 self.size = None @@ -93,20 +98,17 @@ class MyCanvasBase(glcanvas.GLCanvas): def OnEraseBackground(self, event): - pass # Do nothing, to avoid flashing on MSW. - + pass # Do nothing, to avoid flashing on MSW. def OnSize(self, event): wx.CallAfter(self.DoSetViewport) event.Skip() - def DoSetViewport(self): size = self.size = self.GetClientSize() * self.GetContentScaleFactor() self.SetCurrent(self.context) glViewport(0, 0, size.width, size.height) - def OnPaint(self, event): dc = wx.PaintDC(self) self.SetCurrent(self.context) @@ -115,34 +117,79 @@ class MyCanvasBase(glcanvas.GLCanvas): self.init = True self.OnDraw() - - def OnMouseDown(self, evt): + def OnMouseDown(self, event): + if self.HasCapture(): + self.ReleaseMouse() self.CaptureMouse() - self.x, self.y = self.lastx, self.lasty = evt.GetPosition() + self.x, self.y = self.lastx, self.lasty = event.GetPosition() + def OnMouseUp(self, event): + if self.HasCapture(): + self.ReleaseMouse() - def OnMouseUp(self, evt): - self.ReleaseMouse() - - - def OnMouseMotion(self, evt): - if evt.Dragging() and evt.LeftIsDown(): + def OnMouseMotion(self, event): + if event.Dragging() and event.LeftIsDown(): self.lastx, self.lasty = self.x, self.y - self.x, self.y = evt.GetPosition() + self.x, self.y = event.GetPosition() self.Refresh(False) +def ReadTexture(filename): + # Load texture with PIL/PILLOW. RGBA png seems to load fine with pillow. + ## with Image.open(filename) as img: + ## imgWidth, imgHeight = img.size + ## img_data = img.tobytes("raw", "RGB", 0, -1) + + # Load texture with wxPython. + # Hmmm this seems to be wrong channel order or something for wx.Image + # with png alpha when using RGBA. Oh well. We will send jpg Robin instead. + img = wx.Image(filename) + ## if not img.HasAlpha(): + ## img.InitAlpha() + imgWidth, imgHeight = img.GetSize() + img_data = bytes(img.GetData()) + return (imgWidth, imgHeight, img_data) + +def GenerateTexture(imgWidth, imgHeight, img_data): + textureID = glGenTextures(1) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1) + glBindTexture(GL_TEXTURE_2D, textureID) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + + # https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml + target = GL_TEXTURE_2D + level = 0 + internalformat = GL_RGB # GL_RGBA + width = imgWidth + height = imgHeight + border = 0 + format = GL_RGB # GL_RGBA + type = GL_UNSIGNED_BYTE + data = img_data + glTexImage2D(target, level, internalformat, width, height, border, format, type, data) + # https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml + target = GL_TEXTURE_ENV + pname = GL_TEXTURE_ENV_MODE + params = GL_MODULATE + glTexEnvf(target, pname, params) + return textureID + + class CubeCanvas(MyCanvasBase): def InitGL(self): - # set viewing projection + # Set viewing projection. glMatrixMode(GL_PROJECTION) glFrustum(-0.5, 0.5, -0.5, 0.5, 1.0, 3.0) - # position viewer + # Position viewer. glMatrixMode(GL_MODELVIEW) glTranslatef(0.0, 0.0, -2.0) - # position object + # Position object. glRotatef(self.y, 1.0, 0.0, 0.0) glRotatef(self.x, 0.0, 1.0, 0.0) @@ -150,18 +197,24 @@ class CubeCanvas(MyCanvasBase): glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) + self.textureID = None + path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bmp_source')) + rd = 'robin.jpg' + if os.path.exists(os.path.join(path, rd)): + self.textureID = GenerateTexture(*ReadTexture(os.path.join(path, rd))) + def OnDraw(self): - # clear color and depth buffers + # Clear color and depth buffers. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - # draw six faces of a cube + # Draw six faces of a cube. glBegin(GL_QUADS) - glNormal3f( 0.0, 0.0, 1.0) - glVertex3f( 0.5, 0.5, 0.5) - glVertex3f(-0.5, 0.5, 0.5) - glVertex3f(-0.5,-0.5, 0.5) - glVertex3f( 0.5,-0.5, 0.5) + ## glNormal3f( 0.0, 0.0, 1.0) + ## glVertex3f( 0.5, 0.5, 0.5) + ## glVertex3f(-0.5, 0.5, 0.5) + ## glVertex3f(-0.5,-0.5, 0.5) + ## glVertex3f( 0.5,-0.5, 0.5) glNormal3f( 0.0, 0.0,-1.0) glVertex3f(-0.5,-0.5,-0.5) @@ -194,6 +247,21 @@ class CubeCanvas(MyCanvasBase): glVertex3f(-0.5, 0.5,-0.5) glEnd() + if self.textureID: + glEnable(GL_TEXTURE_2D) + ## glBindTexture(GL_TEXTURE_2D, self.textureID) + glBegin(GL_QUADS) + glNormal3f( 0.0, 0.0, 1.0) + glTexCoord2f(0.0, 0.0) + glVertex3fv((0.5, 0.5, 0.5)) + glTexCoord2f(1.0, 0.0) + glVertex3fv((-0.5, 0.5, 0.5)) + glTexCoord2f(1.0, 1.0) + glVertex3fv((-0.5,-0.5, 0.5)) + glTexCoord2f(0.0, 1.0) + glVertex3fv((0.5,-0.5, 0.5)) + glEnd() + if self.size is None: self.size = self.GetClientSize() w, h = self.size @@ -210,7 +278,7 @@ class CubeCanvas(MyCanvasBase): class ConeCanvas(MyCanvasBase): def InitGL( self ): glMatrixMode(GL_PROJECTION) - # camera frustrum setup + # Camera frustrum setup. glFrustum(-0.5, 0.5, -0.5, 0.5, 1.0, 3.0) glMaterial(GL_FRONT, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]) glMaterial(GL_FRONT, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) @@ -226,35 +294,64 @@ class ConeCanvas(MyCanvasBase): glDepthFunc(GL_LESS) glEnable(GL_DEPTH_TEST) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - # position viewer + # Position viewer. glMatrixMode(GL_MODELVIEW) - # position viewer + # Position viewer. glTranslatef(0.0, 0.0, -2.0); - # - glutInit(sys.argv) def OnDraw(self): - # clear color and depth buffers + # Clear color and depth buffers. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - # use a fresh transformation matrix + # Use a fresh transformation matrix. glPushMatrix() - # position object - #glTranslate(0.0, 0.0, -2.0) + # Position object. + ## glTranslate(0.0, 0.0, -2.0) glRotate(30.0, 1.0, 0.0, 0.0) glRotate(30.0, 0.0, 1.0, 0.0) glTranslate(0, -1, 0) glRotate(250, 1, 0, 0) - glutSolidCone(0.5, 1, 30, 5) + + glEnable(GL_BLEND) + glEnable(GL_POLYGON_SMOOTH) + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (0.5, 0.5, 1.0, 0.5)) + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 1.0) + glShadeModel(GL_FLAT) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + # glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + + quad = gluNewQuadric() + base = .5 + top = 0.0 + height = 1.0 + slices = 16 + stacks = 16 + # stacks = 0 + if stacks: + # This is the premade way to make a cone. + gluCylinder(quad, base, top, height, slices, stacks) + else: + # Draw cone open ended without glu. + tau = pi * 2 + glBegin(GL_TRIANGLE_FAN) + centerX, centerY, centerZ = 0.0, 0.0, height + glVertex3f(centerX, centerY, centerZ) # Center of circle. + centerX, centerY, centerZ = 0.0, 0.0, 0.0 + for i in range(slices + 1): + theta = tau * float(i) / float(slices) # Get the current angle. + x = base * cos(theta) # Calculate the x component. + y = base * sin(theta) # Calculate the y component. + glVertex3f(x + centerX, y + centerY, centerZ) # Output vertex. + glEnd() + glPopMatrix() glRotatef((self.y - self.lasty), 0.0, 0.0, 1.0); glRotatef((self.x - self.lastx), 1.0, 0.0, 0.0); - # push into visible buffer + # Push into visible buffer. self.SwapBuffers() - #---------------------------------------------------------------------- @@ -263,13 +360,10 @@ def runTest(frame, nb, log): return win - - overview = """\ """ - if __name__ == '__main__': import sys,os import run