Python OpenGL ES2 on the N900

Yes, i have one and love it. My cute little pocketable linux device. I also like python a lot. Since i program in python i do not like C++ or Java anymore. And i love 3D. Colorfull, programmable and flexible it has to be, exactly like… like OpenGL ES2 Exactly. But does all this fit together?

Of course it does. With the ctypes interface, in principle any system library can be accessed. OpenGL is actually even relatively easy. But where do i get a window to render into from?Xlib is a possibility, but its data structures i did not really want to create ctypes variants for. Fortunately someone else did already. Pyglet is a ctypes based  cross plattform windowing and multimedia library that also containes a wrapper for Xlib. That i used to do a proof of concept port of the  maemo.org SimpleGL example to python.

It is an absolutely minimal port, the EGL/OpenGL i did manualy via ctypes, mapping only whats necessary to get it to run. Pyglet actually contains a script that could be used to create much nicer wrappers from  egl.h und gl2.h. But the beautifying i leave up to you :-P

Here is the port anyway, hope it helps someone.

this is the wrapper from pyglet, i did only do one modification, i directly load libX11.so.6 instead of using using pyglet.lib.load:

xlib wrapper.py

and here is the simple example:

opengltest.py

from ctypes import *
import os
import math
import sys
import time
import xlib_wrapper
egl =  cdll.LoadLibrary( "libEGL.so")
glesv2=  cdll.LoadLibrary( "libGLESv2.so")

#############################################################
#global defs for Opengl
GLuint = c_uint
GLint = c_int
GLenum = c_uint
GLfloat = c_float

GL_TRUE = 1
GL_FALSE = 0
GL_FLOAT = 0x1406

GL_COLOR_BUFFER_BIT = 0x00004000
GL_TRIANGLE_STRIP = 0x0005
GL_TRIANGLE_FAN                   =0x0006

GL_VERTEX_SHADER = 0x8b31
GL_FRAGMENT_SHADER = 0x8b30

GL_COMPILE_STATUS = 0x8b81
GL_INFO_LOG_LENGTH = 0x8b84

##############################################################
#simple shader programs
vertexShaderSrc = c_char_p(
"                                        \
   attribute vec4        position;       \
   varying mediump vec2  pos;            \
   uniform vec4          offset;         \
                                         \
   void main()                           \
   {                                     \
      gl_Position = position + offset;   \
      pos = position.xy;                 \
   }                                     \
")
fragmentShaderSrc = c_char_p(
"                                                      \
   varying mediump vec2    pos;                        \
   uniform mediump float   phase;                      \
                                                       \
   void  main()                                        \
   {                                                   \
      gl_FragColor  =  vec4( 1., 0.9, 0.7, 1.0 ) *     \
        cos( 30.*sqrt(pos.x*pos.x + 1.5*pos.y*pos.y)   \
             + atan(pos.y,pos.x) - phase );            \
   }                                                   \
")
VAType= GLfloat*15
vertexArray = VAType(
   0.0,  0.5,  0.0,
  -0.5,  0.0,  0.0,
   0.0, -0.5,  0.0,
   0.5,  0.0,  0.0
)
updatePos = True
phase = 0.0
norm_x    =  0.0
norm_y    =  0.0
offset_x  =  0.0
offset_y  =  0.0
p1_pos_x  =  0.0
p1_pos_y  =  0.0

##############################################################

def printShaderInfoLog (shader):

   length = GLint()
   glesv2.glGetShaderiv ( shader , GL_INFO_LOG_LENGTH , byref(length) )

   print length.value
   if length.value >0:
      buffer  =  create_string_buffer(length.value)
      glesv2.glGetShaderInfoLog ( shader , length , None , byref(buffer) );
      print "shader info "+str( buffer.value)
      success = GLint();
      glesv2.glGetShaderiv( shader, GL_COMPILE_STATUS, byref(success) )
      if success.value != GL_TRUE :
        print "failed to compile shader"

############################################################

def loadShader ( shader_source, type):
   shader = glesv2.glCreateShader( type );
   glesv2.glShaderSource  ( shader , 1 , byref(shader_source) , None );
   glesv2.glCompileShader ( shader );

   printShaderInfoLog( shader )

   return shader

############################################################

def createOglWindow(name):
  #needs error checking
  display = xlib_wrapper.XOpenDisplay (os.getenv ( "DISPLAY"))
  screen = xlib_wrapper.XDefaultScreen (display)
  root = xlib_wrapper.XDefaultRootWindow (display, screen)
  windowAttributes=xlib_wrapper.XSetWindowAttributes()
  windowAttributes.event_mask = (
               xlib_wrapper.PointerMotionMask
              | xlib_wrapper.KeyPressMask
              | xlib_wrapper.KeyReleaseMask
              | xlib_wrapper.ExposureMask
              | xlib_wrapper.ButtonPressMask
              | xlib_wrapper.ButtonReleaseMask
              )

  window = xlib_wrapper.XCreateWindow(display,root,0,0,800,480,0,
            xlib_wrapper.CopyFromParent,
            xlib_wrapper.InputOutput,
            xlib_wrapper.XDefaultVisual(display,screen),
            xlib_wrapper.CWEventMask,
            byref(windowAttributes) )

  xattr=xlib_wrapper.XSetWindowAttributes()
  xattr.override_redirect=False
  xlib_wrapper.XChangeWindowAttributes(display,window,xlib_wrapper.CWOverrideRedirect,byref(xattr))

  atom=xlib_wrapper.XInternAtom(display,"_NET_WM_STATE_FULLSCREEN",True)
  xlib_wrapper.XChangeProperty(display,window,
            xlib_wrapper.XInternAtom(display,"_NET_WM_STATE",True ),
            xlib_wrapper.XInternAtom(display,"ATOM",False),
            32,
            xlib_wrapper.PropModeReplace,
            cast( pointer((xlib_wrapper.Atom)(atom)), POINTER(c_ubyte)),
            1
            )

  xlib_wrapper.XChangeProperty(display,window,
            xlib_wrapper.XInternAtom(display,"_HILDON_NON_COMPOSITED_WINDOW",True ),
            xlib_wrapper.XInternAtom(display,"INTEGER",False),
            32,
            xlib_wrapper.PropModeReplace,
            cast( pointer(c_ulong(1)), POINTER(c_ubyte)),
            1
            )
  xlib_wrapper.XMapWindow(display, window)
  xlib_wrapper.XStoreName(display,window,name)

  wm_state=xlib_wrapper.XInternAtom(display,"_NET_WM_STATE",False)
  fullscreen=xlib_wrapper.XInternAtom(display,"_NET_WM_STATE_FULLSCREEN",False)

  xev = xlib_wrapper.XEvent()
  xev.type=xlib_wrapper.ClientMessage
  xev.xclient.window=window
  xev.xclient.message_type=wm_state
  xev.xclient.format=32
  xev.xclient.data.l[0]=1
  xev.xclient.data.l[1]=fullscreen
  xlib_wrapper.XSendEvent(display,root,False,xlib_wrapper.SubstructureNotifyMask,byref(xev))

  #now for the egl part
  #needs error checking
  eglDisplay= egl.eglGetDisplay(display)
  egl.eglInitialize(eglDisplay,None,None)
  Attr5=c_ulong*5
  attr=Attr5(
         0x3020,#EGL_BUFFER_SIZE
         16,
         0x3040,#EGL_RENDERABLE_TYPE
         0x0004,#EGL_OPENGL_ES2_BIT
         0x3038 #EGL_NONE
         )
  numConfig=c_ulong()
  ecfg=c_void_p()
  egl.eglChooseConfig(eglDisplay,attr,byref(ecfg),1,byref(numConfig))
  eglSurface=egl.eglCreateWindowSurface(eglDisplay,ecfg,window,None,)
  Attr3=c_ulong*3
  ctxattr=Attr3(
         0x3098,#EGL_CONTEXR_CLIENT_VERSION
         2,
         0x3038 #EGL_NONE
         )
  eglContext=egl.eglCreateContext(eglDisplay,ecfg,0,ctxattr)
  egl.eglMakeCurrent( eglDisplay, eglSurface, eglSurface, eglContext );
  xlib_wrapper.XSetInputFocus(display,root,xlib_wrapper.RevertToParent,xlib_wrapper.CurrentTime)
  xlib_wrapper.XSetInputFocus(display,window,xlib_wrapper.RevertToParent,xlib_wrapper.CurrentTime)

  return display,window,eglDisplay,eglSurface,eglContext

##############################################################

def render():
   global phase
   global updatePos
   global offset_x
   global offset_y
   global p1_pos_x
   global p1_pos_y

   glesv2.glClear ( GL_COLOR_BUFFER_BIT )

   glesv2.glUniform1f ( phaseLoc , GLfloat(phase) )
   phase  =  math.fmod ( phase + 0.5 , 2. * 3.141 )    

   if updatePos == True:
     old_offset_x  =  offset_x
     old_offset_y  =  offset_y

     offset_x  =  norm_x - p1_pos_x
     offset_y  =  norm_y - p1_pos_y

     p1_pos_x  =  norm_x
     p1_pos_y  =  norm_y

     offset_x  +=  old_offset_x
     offset_y  +=  old_offset_y

     updatePos = False

   glesv2.glUniform4f ( offsetLoc  ,  GLfloat(offset_x) , GLfloat(offset_y) , GLfloat(0.0) , GLfloat(0.0) )

   glesv2.glVertexAttribPointer ( positionLoc, 3, GL_FLOAT, GL_FALSE, 0, vertexArray );
   glesv2.glEnableVertexAttribArray ( positionLoc );
   glesv2.glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 );

   egl.eglSwapBuffers ( eglDisplay, eglSurface );  

##############################################################
def cleanUp():
  egl.eglDestroyContext(eglDisplay,eglContext)
  egl.eglDestroySurface(eglDisplay,eglSurface)
  egl.eglTerminate(eglDisplay)
  xlib_wrapper.XDestroyWindow(xDisplay,xWindow)
  xlib_wrapper.XCloseDisplay(xDisplay)

##############################################################  

#ceate a suitable window
xDisplay,xWindow,eglDisplay,eglSurface,eglContext = createOglWindow("test")
#now the ogl parts-----------------------

#set up shader
vertexShader=loadShader(vertexShaderSrc,GL_VERTEX_SHADER)
fragmentShader=loadShader(fragmentShaderSrc,GL_FRAGMENT_SHADER)
shaderProgram=glesv2.glCreateProgram()
glesv2.glAttachShader(shaderProgram,vertexShader)
glesv2.glAttachShader(shaderProgram,fragmentShader)
glesv2.glLinkProgram(shaderProgram)
glesv2.glUseProgram(shaderProgram)
#location of shader params
positionLoc  = glesv2.glGetAttribLocation(shaderProgram,c_char_p("position") )
phaseLoc     = glesv2.glGetUniformLocation(shaderProgram,c_char_p("phase")    )
offsetLoc    = glesv2.glGetUniformLocation(shaderProgram,c_char_p("offset")   )
print str(positionLoc) + str(phaseLoc) + str(offsetLoc)            

window_width  = 800.0
window_height = 480.0

gwa =  xlib_wrapper.XWindowAttributes()
xlib_wrapper.XGetWindowAttributes ( xDisplay , xWindow , byref(gwa) )
glesv2.glViewport ( 0 , 0 , gwa.width , gwa.height )
glesv2.glClearColor ( GLfloat(0.308) , GLfloat(0.06) , GLfloat(0.07) , GLfloat(1.))

quit = False
while quit == False:
  while xlib_wrapper.XPending ( xDisplay ):
    xev = xlib_wrapper.XEvent()
    xlib_wrapper.XNextEvent( xDisplay, byref(xev) )
    print ""
    print xev.type
    print ""
    if xev.type == xlib_wrapper.MotionNotify:
      print "moveto " + str(xev.xmotion.x) + "," + str(xev.xmotion.y)
      window_y  =  (window_height - xev.xmotion.y) - window_height / 2.0;
      norm_y            =  window_y / (window_height / 2.0)
      window_x  =  xev.xmotion.x - window_width / 2.0
      norm_x            =  window_x / (window_width / 2.0)
      updatePos = True

    if xev.type == xlib_wrapper.KeyPress:
      print "keypress"
      quit = True

  render()

#end and cleanup------------------------
time.sleep (1)
cleanUp()
print "done"

Tags: ,

  1. ukaef’s avatar

    Thank you sooo much for this!

  2. huhu’s avatar

    Thx so much!!!