dberube4

Pyglet+PyMunk+GLSL

Sep 15th, 2013
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.46 KB | None | 0 0
  1. from pyglet.window import Window, key
  2. from pyglet.graphics import vertex_list, Batch
  3. from pyglet import app, clock, gl
  4. from pyglet.gl import *
  5.  
  6. from ctypes import *
  7.  
  8. import pymunk
  9. from pymunk import Vec2d
  10.  
  11. from mathutils import Matrix, Vector
  12. from math import radians
  13.  
  14. from random import choice, randint
  15.  
  16. #The vertex shader
  17. vertex_shader = b'''
  18. uniform mat4 translation;
  19. uniform mat4 orientation;
  20.  
  21. void main()
  22. {
  23.    vec4 pos = gl_Vertex*orientation*translation;
  24.    gl_Position = gl_ModelViewProjectionMatrix*pos;
  25. }
  26. '''
  27.  
  28. #The fragment shader
  29. frag_shader = b'''
  30. uniform vec4 color;
  31.  
  32. void main()
  33. {
  34.        gl_FragColor = color;
  35. }
  36. '''
  37.  
  38. class Shader:
  39. #
  40. # Copyright Tristam Macdonald 2008.
  41. #
  42. # Distributed under the Boost Software License, Version 1.0
  43. # (see http://www.boost.org/LICENSE_1_0.txt)
  44. #
  45.  
  46.     # vert, frag and geom take arrays of source strings
  47.     # the arrays will be concattenated into one string by OpenGL
  48.     def __init__(self, vert = [], frag = [], geom = []):
  49.         # create the program handle
  50.         self.handle = glCreateProgram()
  51.         # we are not linked yet
  52.         self.linked = False
  53.  
  54.         # create the vertex shader
  55.         self.createShader(vert, GL_VERTEX_SHADER)
  56.         # create the fragment shader
  57.         self.createShader(frag, GL_FRAGMENT_SHADER)
  58.         # the geometry shader will be the same, once pyglet supports the extension
  59.         # self.createShader(frag, GL_GEOMETRY_SHADER_EXT)
  60.  
  61.         # attempt to link the program
  62.         self.link()
  63.  
  64.     def createShader(self, strings, type):
  65.         count = len(strings)
  66.         # if we have no source code, ignore this shader
  67.         if count < 1:
  68.             return
  69.  
  70.         # create the shader handle
  71.         shader = glCreateShader(type)
  72.  
  73.         # convert the source strings into a ctypes pointer-to-char array, and upload them
  74.         # this is deep, dark, dangerous black magick - don't try stuff like this at home!
  75.         src = (c_char_p * count)(*strings)
  76.         glShaderSource(shader, count, cast(pointer(src), POINTER(POINTER(c_char))), None)
  77.  
  78.         # compile the shader
  79.         glCompileShader(shader)
  80.  
  81.         temp = c_int(0)
  82.         # retrieve the compile status
  83.         glGetShaderiv(shader, GL_COMPILE_STATUS, byref(temp))
  84.  
  85.         # if compilation failed, print the log
  86.         if not temp:
  87.             # retrieve the log length
  88.             glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(temp))
  89.             # create a buffer for the log
  90.             buffer = create_string_buffer(temp.value)
  91.             # retrieve the log text
  92.             glGetShaderInfoLog(shader, temp, None, buffer)
  93.             # print the log to the console
  94.             print (buffer.value)
  95.         else:
  96.             # all is well, so attach the shader to the program
  97.             glAttachShader(self.handle, shader);
  98.  
  99.     def link(self):
  100.         # link the program
  101.         glLinkProgram(self.handle)
  102.  
  103.         temp = c_int(0)
  104.         # retrieve the link status
  105.         glGetProgramiv(self.handle, GL_LINK_STATUS, byref(temp))
  106.  
  107.         # if linking failed, print the log
  108.         if not temp:
  109.             #   retrieve the log length
  110.             glGetProgramiv(self.handle, GL_INFO_LOG_LENGTH, byref(temp))
  111.             # create a buffer for the log
  112.             buffer = create_string_buffer(temp.value)
  113.             # retrieve the log text
  114.             glGetProgramInfoLog(self.handle, temp, None, buffer)
  115.             # print the log to the console
  116.             print (buffer.value)
  117.         else:
  118.             # all is well, so we are linked
  119.             self.linked = True
  120.  
  121.     def bind(self):
  122.         # bind the program
  123.         glUseProgram(self.handle)
  124.  
  125.     def unbind(self):
  126.         # unbind whatever program is currently bound - not necessarily this program,
  127.         # so this should probably be a class method instead
  128.         glUseProgram(0)
  129.  
  130.     # upload a floating point uniform
  131.     # this program must be currently bound
  132.     def uniformf(self, name, *vals):
  133.         # check there are 1-4 values
  134.         if len(vals) in range(1, 5):
  135.             # select the correct function
  136.             { 1 : glUniform1f,
  137.                 2 : glUniform2f,
  138.                 3 : glUniform3f,
  139.                 4 : glUniform4f
  140.                 # retrieve the uniform location, and set
  141.             }[len(vals)](glGetUniformLocation(self.handle, name), *vals)
  142.  
  143.     # upload an integer uniform
  144.     # this program must be currently bound
  145.     def uniformi(self, name, *vals):
  146.         # check there are 1-4 values
  147.         if len(vals) in range(1, 5):
  148.             # select the correct function
  149.             { 1 : glUniform1i,
  150.                 2 : glUniform2i,
  151.                 3 : glUniform3i,
  152.                 4 : glUniform4i
  153.                 # retrieve the uniform location, and set
  154.             }[len(vals)](glGetUniformLocation(self.handle, name), *vals)
  155.  
  156.     # upload a uniform matrix
  157.     # works with matrices stored as lists,
  158.     # as well as euclid matrices
  159.     def uniform_matrixf(self, name, mat):
  160.         # obtian the uniform location
  161.         loc = glGetUniformLocation(self.handle, name)
  162.         # uplaod the 4x4 floating point matrix
  163.         glUniformMatrix4fv(loc, 1, False, (c_float * 16)(*mat))
  164.  
  165. class NoCollisionRectangle():
  166.     '''
  167.    An object that wrap the drawings of a rectangle.
  168.    '''
  169.     def __init__(self, x, y, width, height, color=[0,0,0]):
  170.         vertices = [x,y, x+width,y, x+width,y+height, x,y+height]
  171.         self.array = vertex_list(4, ("v2f", vertices), ("c3B", color*4))
  172.        
  173.         self.position = (x, y)
  174.         self.size = (width, height)
  175.         self.color = color
  176.         self.registered = False
  177.        
  178.        
  179.     def move(self, x, y):
  180.        
  181.         for i in range(4):
  182.             self.array.vertices[i*2] += x
  183.             self.array.vertices[i*2+1] += y
  184.            
  185.     def draw(self):
  186.         self.array.draw(gl.GL_QUADS)
  187.  
  188.     def register(self, batch, group=None):
  189.         vertices = list(self.array.vertices)
  190.         colors = list(self.array.colors)
  191.         self.array = batch.add(4, gl.GL_QUADS, group, ("v2f", vertices), ("c3B", colors))
  192.         self.registered = True
  193.        
  194. class StaticLine(object):
  195.     '''
  196.    An object that bind drawing and physics of a static line.
  197.    '''
  198.     def __init__(self, start, end, body, color=[0,0,0], friction=0.0):
  199.         '''
  200.        start(suple): A tuple of 2D vertex coordinate. Start point of the line.
  201.        end(tuple): A tuple of 2D vertex coordinate. End point of the line.
  202.        body(pymunk.Body): Pymunk body object
  203.        color(list): List of [red green blue] in bytes. Color of the vertices.
  204.        friction(float): Friction for the physic.
  205.        '''
  206.        
  207.         vertices = (start[0], start[1], end[0], end[1])
  208.         self.mesh = vertex_list(2, ('v2i', vertices),
  209.                                    ('c3B', color*2 ))
  210.         self.mesh_physic = pymunk.Segment(body, start, end, 0.0)
  211.         self.friction = friction
  212.        
  213.     def registerPhysicSpace(self, space):
  214.         space.add(self.mesh_physic)
  215.        
  216.     def draw(self):
  217.         self.mesh.draw(gl.GL_LINES)
  218.        
  219. class DynamicRectangle():
  220.     '''
  221.    An object that wrap the drawings and the physic of a rectangle.
  222.    Instead of being handled by python, the drawing are done in GLSL
  223.    on the GPU.
  224.    '''
  225.     def __init__(self, x, y, width, height, mass = 10, friction=1, color=[0,0,0,1]):
  226.        
  227.         #Properties
  228.         self.shader = None
  229.         self.color = color
  230.         self.position = Matrix.Translation((x, y, 0.0))
  231.         self.orientation = Matrix.Rotation(radians(0), 4, 'Z')
  232.        
  233.         #Visual Shape
  234.         w,h = width/2, height/2
  235.         vertices = [-w,-h, w,-h, w,h, -w,h]
  236.         self.array = vertex_list(4, ("v2f", vertices))
  237.        
  238.         #Physic shape
  239.         moment = pymunk.moment_for_box(mass, w, h)
  240.         self.body = pymunk.Body(mass, moment)
  241.         self.mesh_physic = pymunk.Poly.create_box(self.body, (w,h), radius = w/2)
  242.         self.mesh_physic.friction = friction
  243.         self.body.position = x+w/2, y+h/2
  244.        
  245.     def registerShader(self, vertex, fragment):
  246.         self.shader = Shader([vertex], [fragment])
  247.        
  248.     def draw(self):
  249.        
  250.         def toList(mat):
  251.             final = []
  252.             for vect in mat:
  253.                 final+=list(vect)
  254.             return final
  255.        
  256.         #Update physics
  257.         self.position = Matrix.Translation(list(self.body.position)+[0])
  258.         self.orientation = Matrix.Rotation(self.body.angle, 4, 'Z')
  259.        
  260.         if self.shader: #Object need a shader to be drawn
  261.             self.shader.bind()
  262.             self.shader.uniformf(b"color", *self.color)
  263.             self.shader.uniform_matrixf(b"translation", toList(self.position))
  264.             self.shader.uniform_matrixf(b"orientation", toList(self.orientation))
  265.             self.array.draw(GL_QUADS)
  266.             self.shader.unbind()
  267.    
  268.     def registerPhysicSpace(self, space):
  269.         space.add(self.body, self.mesh_physic)
  270.    
  271.     def applyMovement(self, x, y):
  272.         self.position *= Matrix.Translation((x,y,0))
  273.        
  274.     def setPosition(self, x,y):
  275.         self.position = Matrix.Translation((x,y,0))
  276.        
  277.     def applyRotation(self, rot):
  278.         self.orientation *= Matrix.Rotation(radians(rot), 4, 'Z')
  279.    
  280.     def setOrientation(self, rot):
  281.         self.orientation = Matrix.Rotation(radians(rot), 4, 'Z')
  282.  
  283. class Game(Window):
  284.    
  285.     def __init__(self, **kwargs):
  286.         super(Game, self).__init__(**kwargs)
  287.        
  288.         self.initGame()        
  289.        
  290.         self.set_visible(True)
  291.    
  292.     def initGame(self):
  293.        
  294.         w,h = self.width, self.height
  295.        
  296.         #Objects Lists
  297.         self.statics = []
  298.         self.dynamics = []        
  299.        
  300.         #Pymunk space
  301.         self.space = pymunk.Space()
  302.         self.space.gravity = Vec2d(0, -900)        
  303.        
  304.         #Pyglet batch
  305.         self.batch = Batch()
  306.        
  307.         #Background & Ground
  308.         self.background = NoCollisionRectangle(0,0, w,h, color=[255,255,255])
  309.         self.background.register(self.batch)
  310.        
  311.         #Lines
  312.         body = pymunk.Body()
  313.         l1 = StaticLine((0,50), (w,50), body)
  314.         l2 = StaticLine((30,50), (30,170), body)
  315.         l3 = StaticLine((w-30,50), (w-30,170), body)
  316.         l4 = StaticLine((90,180), (130,100), body)
  317.        
  318.         self.statics = [l1, l2, l3, l4]
  319.         for i in self.statics:
  320.             i.registerPhysicSpace(self.space)
  321.    
  322.     def on_draw(self,):
  323.         self.clear()
  324.         self.batch.draw()
  325.        
  326.         for i in self.statics:
  327.             i.draw()
  328.            
  329.         for i in self.dynamics:
  330.             i.draw()
  331.    
  332.     def on_mouse_press(self, x,y, button, mod):
  333.        
  334.         color = choice([[0,0,0,1], [1,0,0,1], [0,1,0,1], [0,0,1,1], [1,1,0,1], [1,0,1,1],
  335.                    [0,1,1,1]])
  336.         size = randint(10, 35)
  337.        
  338.         if button == 1:
  339.             d = DynamicRectangle(x, y, size, size, color = color)
  340.             d.registerPhysicSpace(self.space)
  341.             d.registerShader(vertex_shader, frag_shader)
  342.             self.dynamics.append(d)
  343.            
  344.     def update(self, dt):
  345.        
  346.         dt = 1.0/60.0
  347.         self.space.step(dt) #Physics steps        
  348.        
  349.         self.on_draw()
  350.  
  351. if __name__ == '__main__':
  352.     w = Game(caption="Glsl Test", width=500, height=500, visible = False)
  353.     clock.schedule_interval(w.update, 1.0/45.0)
  354.     app.run()
Advertisement
Add Comment
Please, Sign In to add comment