class CoreTriangle( object ): vertex_shader = textwrap.dedent( """ #version 150 // input in vec3 in_position; in vec3 in_colour; uniform mat4 model_view; uniform mat4 projection; // shared out vec3 ex_colour; void main(void) { // apply projection and model view matrix to vertex gl_Position = projection * model_view * vec4( in_position, 1.0 ); ex_colour = in_colour; } """ ) fragment_shader = textwrap.dedent( """ #version 150 // shared in vec3 ex_colour; // output out vec4 fragColor; void main(void) { // set colour of each fragment fragColor = vec4( ex_colour, 1.0 ); } """ ) def __init__( self ): super( CoreTriangle, self ).__init__() global vertices # create our shader self.shader = ShaderProgram( VertexShader( self.vertex_shader ), FragmentShader( self.fragment_shader ), ) # create a vertex buffer # we pass in a list of regions we want to define # we only have 1 region here # for each region, we pass in how many rows, and the dtype self.buffer = VertexBuffer( GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW, data = vertices, ) # pass the shader and region to our VAO # and bind each of the attributes to a VAO index # the shader name is the variable name used in the shader # the buffer name is the name of the property in our vertex dtype self.buffer_attributes = BufferAttributes() self.buffer_attributes[ 'position' ] = GenericAttribute.from_dtype( self.buffer, vertices.dtype, 'position', location = self.shader.attributes[ 'in_position' ] ) self.buffer_attributes[ 'colour' ] = GenericAttribute.from_dtype( self.buffer, vertices.dtype, 'colour', location = self.shader.attributes[ 'in_colour' ] ) # create our vertex array self.vao = VertexArray() self.vao.bind() self.buffer.bind() self.buffer_attributes.set() self.buffer.unbind() self.vao.unbind() def draw( self, projection, model_view ): global vertices self.shader.bind() self.shader.uniforms[ 'projection' ].value = projection self.shader.uniforms[ 'model_view' ].value = model_view self.vao.bind() GL.glDrawArrays( GL.GL_TRIANGLES, 0, len( vertices ) ) self.vao.unbind() self.shader.unbind()
class CoreCube(object): vertex_shader = textwrap.dedent(""" #version 150 // input in vec3 in_position; in vec3 in_colour; uniform mat4 model_view; uniform mat4 projection; // shared out vec3 ex_colour; void main(void) { // apply projection and model view matrix to vertex gl_Position = projection * model_view * vec4( in_position, 1.0 ); ex_colour = in_colour; } """) fragment_shader = textwrap.dedent(""" #version 150 // shared in vec3 ex_colour; // output out vec4 fragColor; void main(void) { // set colour of each fragment fragColor = vec4( ex_colour, 1.0 ); } """) def __init__(self): super(CoreCube, self).__init__() global vertices # create our shader self.shader = ShaderProgram(VertexShader(self.vertex_shader), FragmentShader(self.fragment_shader)) # create a vertex buffer # we pass in a list of regions we want to define # we only have 1 region here # for each region, we pass in how many rows, and the dtype self.buffer = VertexBuffer( GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW, data=vertices, ) # pass the shader and region to our VAO # and bind each of the attributes to a VAO index # the shader name is the variable name used in the shader # the buffer name is the name of the property in our vertex dtype self.buffer_attributes = BufferAttributes() self.buffer_attributes['position'] = GenericAttribute.from_dtype( self.buffer, vertices.dtype, 'position', location=self.shader.attributes['in_position']) self.buffer_attributes['colour'] = GenericAttribute.from_dtype( self.buffer, vertices.dtype, 'colour', location=self.shader.attributes['in_colour']) # create our vertex array self.vao = VertexArray() self.vao.bind() self.buffer.bind() self.buffer_attributes.set() self.buffer.unbind() self.vao.unbind() def draw(self, projection, model_view): global vertices self.shader.bind() self.shader.uniforms['projection'].value = projection self.shader.uniforms['model_view'].value = model_view self.vao.bind() GL.glDrawArrays(GL.GL_TRIANGLES, 0, len(vertices)) self.vao.unbind() self.shader.unbind()
class LegacyTriangle( object ): vertex_shader = textwrap.dedent( """ #version 120 void main(void) { // apply projection and model view matrix to vertex gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; gl_FrontColor = gl_Color; } """ ) fragment_shader = textwrap.dedent( """ #version 120 void main(void) { // set colour of each fragment gl_FragColor = gl_Color; } """ ) def __init__( self ): super( LegacyTriangle, self ).__init__() global vertices self.use_shaders = True # create our shader self.shader = ShaderProgram( VertexShader( self.vertex_shader ), FragmentShader( self.fragment_shader ) ) # create our vertex buffer self.buffer = VertexBuffer( GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW, data = vertices, ) self.buffer_attributes = BufferAttributes() self.buffer_attributes[ 'position' ] = VertexAttribute.from_dtype( self.buffer, vertices.dtype, 'position' ) self.buffer_attributes[ 'colour' ] = ColourAttribute.from_dtype( self.buffer, vertices.dtype, 'colour' ) def draw( self ): global vertices if self.use_shaders: self.shader.bind() self.buffer_attributes.push_attributes() # set the vertex pointer to the position data self.buffer.bind() self.buffer_attributes.set() self.buffer.unbind() GL.glDrawArrays( GL.GL_TRIANGLES, 0, len( vertices ) ) self.buffer_attributes.pop_attributes() if self.use_shaders: self.shader.unbind()
class Mesh( Mesh ): shader_source = { 'vert': open(os.path.dirname(__file__) + '/md5.vert','r').read(), 'frag': open(os.path.dirname(__file__) + '/md5.frag','r').read(), } def __init__( self, md5mesh ): super( Mesh, self ).__init__() self.mesh = MeshData( md5mesh ) self.vbo = (GLuint)() self.tbo = (GLuint)() self.shader = None glGenBuffers( 1, self.vbo ) glGenTextures( 1, self.tbo ) self.shader = ShaderProgram( Shader( GL_VERTEX_SHADER, Mesh.shader_source['vert'] ), Shader( GL_FRAGMENT_SHADER, Mesh.shader_source['frag'] ), link_now = False ) # set our shader data # we MUST do this before we link the shader self.shader.attributes.in_normal = 0 self.shader.attributes.in_texture_coord = 1 self.shader.attributes.in_bone_indices = 2 self.shader.attributes.in_bone_weights_1 = 3 self.shader.attributes.in_bone_weights_2 = 4 self.shader.attributes.in_bone_weights_3 = 5 self.shader.attributes.in_bone_weights_4 = 6 self.shader.frag_location( 'out_frag_colour' ) # link the shader now self.shader.link() # bind our uniform indices self.shader.bind() self.shader.uniforms.in_diffuse = 0 self.shader.uniforms.in_specular = 1 self.shader.uniforms.in_normal = 2 self.shader.uniforms.in_bone_matrices = 4 self.shader.unbind() def set_skeleton( self, skeleton ): # load the matrices into our texture buffer #matrices = skeleton.matrices matrices = numpy.zeros( (skeleton.num_joints, 2, 4), dtype = 'float32' ) matrices[ :, 0 ] = skeleton.orientations matrices[ :, 1, 0:3 ] = skeleton.positions glBindBuffer( GL_TEXTURE_BUFFER, self.vbo ) glBufferData( GL_TEXTURE_BUFFER, matrices.nbytes, (GLfloat * matrices.size)(*matrices.flat), GL_STATIC_DRAW ) # link to our BO glBindTexture( GL_TEXTURE_BUFFER, self.tbo ) glTexBuffer( GL_TEXTURE_BUFFER, GL_RGBA32F, self.vbo ) glBindTexture( GL_TEXTURE_BUFFER, 0 ) glBindBuffer( GL_TEXTURE_BUFFER, 0 ) def render( self, projection, model_view ): # bind our shader and pass in our model view self.shader.bind() self.shader.uniforms.in_model_view = model_view self.shader.uniforms.in_projection = projection # set our animation data glActiveTexture( GL_TEXTURE0 + 4 ) glBindTexture( GL_TEXTURE_BUFFER, self.tbo ) # render the mesh self.mesh.render() # restore state glActiveTexture( GL_TEXTURE0 + 3 ) glBindTexture( GL_TEXTURE_BUFFER, 0 ) glActiveTexture( GL_TEXTURE0 ) self.shader.unbind()
class LegacyCube(object): vertex_shader = textwrap.dedent(""" #version 120 void main(void) { // apply projection and model view matrix to vertex gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; gl_FrontColor = gl_Color; } """) fragment_shader = textwrap.dedent(""" #version 120 void main(void) { // set colour of each fragment gl_FragColor = gl_Color; } """) def __init__(self): super(LegacyCube, self).__init__() global vertices self.use_shaders = True # create our shader self.shader = ShaderProgram(VertexShader(self.vertex_shader), FragmentShader(self.fragment_shader)) # create our vertex buffer self.buffer = VertexBuffer( GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW, data=vertices, ) self.buffer_attributes = BufferAttributes() self.buffer_attributes['position'] = VertexAttribute.from_dtype( self.buffer, vertices.dtype, 'position') self.buffer_attributes['colour'] = ColourAttribute.from_dtype( self.buffer, vertices.dtype, 'colour') def draw(self): global vertices if self.use_shaders: self.shader.bind() self.buffer_attributes.push_attributes() # set the vertex pointer to the position data self.buffer.bind() self.buffer_attributes.set() self.buffer.unbind() GL.glDrawArrays(GL.GL_TRIANGLES, 0, len(vertices)) self.buffer_attributes.pop_attributes() if self.use_shaders: self.shader.unbind()
class SkeletonRenderer(object): shader_source = { 'vert': open(os.path.dirname(__file__) + '/skeleton.vert', 'r').read(), 'frag': open(os.path.dirname(__file__) + '/skeleton.frag', 'r').read(), } def __init__(self): super(SkeletonRenderer, self).__init__() self.num_joints = None self.shader = None self.vao = (GLuint)() self.indices_vbo = (GLuint)() self.matrix_vbo = (GLuint)() self.matrix_tbo = (GLuint)() # load our shader self.shader = ShaderProgram( Shader(GL_VERTEX_SHADER, SkeletonRenderer.shader_source['vert']), Shader(GL_FRAGMENT_SHADER, SkeletonRenderer.shader_source['frag']), link_now=False) # set our shader data # we MUST do this before we link the shader self.shader.attributes.in_index = 0 self.shader.frag_location('out_frag_colour') # link the shader now self.shader.link() # bind our uniform indices self.shader.bind() self.shader.uniforms.in_bone_matrices = 0 self.shader.unbind() # generate our buffers glGenVertexArrays(1, self.vao) glGenBuffers(1, self.indices_vbo) glGenBuffers(1, self.matrix_vbo) glGenTextures(1, self.matrix_tbo) def set_skeleton(self, skeleton): self.num_joints = skeleton.num_joints # bone indices # create a skeleton from our bones lines = [] for index, joint in enumerate(skeleton): if joint.parent >= 0: lines.append([joint.parent, index]) else: lines.append([index, index]) np_lines = numpy.array(lines, dtype='uint32') # setup our VAO glBindVertexArray(self.vao) glBindBuffer(GL_ARRAY_BUFFER, self.indices_vbo) glBufferData(GL_ARRAY_BUFFER, np_lines.nbytes, (GLuint * np_lines.size)(*np_lines.flat), GL_DYNAMIC_DRAW) glEnableVertexAttribArray(0) glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, GL_FALSE, 0, 0) glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) # bone matrices # load the matrices into our texture buffer #matrices = skeleton.matrices matrices = numpy.zeros((skeleton.num_joints, 2, 4), dtype='float32') matrices[:, 0] = skeleton.orientations matrices[:, 1, 0:3] = skeleton.positions glBindBuffer(GL_TEXTURE_BUFFER, self.matrix_vbo) glBufferData(GL_TEXTURE_BUFFER, matrices.nbytes, (GLfloat * matrices.size)(*matrices.flat), GL_STATIC_DRAW) # link to our BO glBindTexture(GL_TEXTURE_BUFFER, self.matrix_tbo) glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, self.matrix_vbo) glBindTexture(GL_TEXTURE_BUFFER, 0) glBindBuffer(GL_TEXTURE_BUFFER, 0) def render(self, projection, model_view): if self.num_joints == None: raise ValueError("Skeleton not initialised") self.shader.bind() self.shader.uniforms.in_model_view = model_view self.shader.uniforms.in_projection = projection glBindVertexArray(self.vao) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_BUFFER, self.matrix_tbo) glDrawArrays(GL_LINES, 0, self.num_joints * 2) glBindVertexArray(0) self.shader.unbind()
class Data( object ): shader_source = { 'vert': open(os.path.dirname(__file__) + '/obj.vert','r').read(), 'frag': open(os.path.dirname(__file__) + '/obj.frag','r').read() } _data = {} @classmethod def load( cls, filename ): # check if the model has been loaded previously if filename in Data._data: # create a new mesh with the same data return Data._data[ filename ] data = cls( filename ) # store mesh for later Data._data[ filename ] = data return data @classmethod def unload( cls, filename ): if filename in Data._data: del Data._data[ filename ] def __init__( self, filename = None, buffer = None ): super( Data, self ).__init__() self.meshes = {} # create our shader self.shader = ShaderProgram( Shader( GL_VERTEX_SHADER, Data.shader_source['vert'] ), Shader( GL_FRAGMENT_SHADER, Data.shader_source['frag'] ), link_now = False ) # set our shader data # we MUST do this before we link the shader self.shader.attributes.in_position = 0 self.shader.attributes.in_texture_coord = 1 self.shader.attributes.in_normal = 2 self.shader.frag_location( 'out_frag_colour' ) # link the shader now self.shader.link() # bind our uniform indices self.shader.bind() self.shader.uniforms.tex0 = 0 self.shader.unbind() self.obj = pymesh.obj.OBJ() if filename != None: self.obj.load( filename ) else: self.obj.load_from_buffer( buffer ) self._load() def _load( self ): """ Processes the data loaded by the MD2 Loader """ # convert the md2 data into data for the gpu # first, load our vertex buffer objects self._load_vertex_buffers() def _load_vertex_buffers( self ): # we need to convert from 3 lists with 3 sets of indices # to 3 lists with 1 set of indices # so for each index, we need to check if we already # have a matching vertex, and if not, make one vertex_bin = OrderedDict([]) vertices = [] texture_coords = [] normals = [] def process_vertex_data( bin, vertices, texture_coords, normals, data ): # check if we've already got this unique vertex in our list if data not in bin: # the vertex doesn't exist yet # insert into our vertex bin bin[ data ] = len(bin) # convert our indices into actual data v_index, tc_index, n_index = data vertices.extend( list(self.obj.model.vertices[ v_index ]) ) # map our texture coordinates # if no tc is present, insert 0.0, 0.0 if tc_index != None: texture_coords.extend( list(self.obj.model.texture_coords[ tc_index ]) ) else: texture_coords.extend( [0.0, 0.0] ) # map our normals # if no normal is present, insert 0.0, 0.0, 0.0 if n_index != None: normals.extend( list(self.obj.model.normals[ n_index ]) ) else: normals.extend( [0.0, 0.0, 0.0] ) # return the new index return bin[ data ] for mesh in self.obj.model.meshes: indices = [] num_points = 0 num_lines = 0 num_faces = 0 # check if we need to create a point mesh if len(mesh['points']) > 0: # remap each point from a random set of indices # to a unique vertex for point in mesh['points']: indices.append( process_vertex_data( vertex_bin, vertices, texture_coords, normals, point ) ) num_points = len(mesh['points']) # check if we need to create a line mesh if len(mesh['lines']) > 0: # each line tuple is a line strip # the easiest way to render is to convert to # line segments def convert_to_lines( strip ): result = [] previous = strip[ 0 ] for point in strip[ 1: ]: result.extend( [previous, point] ) previous = point return result # convert each line strip into line segments line_segments = [] for strip in mesh['lines']: line_segments.extend( convert_to_lines( strip ) ) # remap each point from a random set of indices # to a unique vertex for point in line_segments: indices.append( process_vertex_data( vertex_bin, vertices, texture_coords, normals, point ) ) num_lines = len(line_segments) # check if we need to create a face mesh if len(mesh['faces']) > 0: # faces are stored as a list of triangle fans # we need to covnert them to triangles def convert_to_triangles( fan ): # convert from triangle fan # 0, 1, 2, 3, 4, 5 # to triangle list # 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5 result = [] start = fan[ 0 ] previous = fan[ 1 ] for point in fan[ 2: ]: result.extend( [start, previous, point ] ) previous = point return result # convert each triangle face to triangles triangle_indices = [] for face in mesh['faces']: triangle_indices.extend( convert_to_triangles( face ) ) # remap each point from a random set of indices # to a unique vertex for point in triangle_indices: indices.append( process_vertex_data( vertex_bin, vertices, texture_coords, normals, point ) ) num_faces = len(triangle_indices) # create our index arrays element_vbo = (GLuint)() glGenBuffers( 1, element_vbo ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, element_vbo ) glBufferData( GL_ELEMENT_ARRAY_BUFFER, len(indices) * 4, (GLuint * len(indices))(*indices), GL_STATIC_DRAW ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ) # store our indices gl_data = ( element_vbo, (0, num_points), (num_points, num_lines), (num_points + num_lines, num_faces) ) # add the mesh to each of the mesh groups # each group has a list of meshes it owns for group in mesh['groups']: if group not in self.meshes: self.meshes[ group ] = [] self.meshes[ group ].append( gl_data ) self.vao = (GLuint)() glGenVertexArrays( 1, self.vao ) glBindVertexArray( self.vao ) # create our global vertex data self.vbo = (GLuint * 3)() glGenBuffers( 3, self.vbo ) # create a VBO for our vertices glBindBuffer( GL_ARRAY_BUFFER, self.vbo[ 0 ] ) glBufferData( GL_ARRAY_BUFFER, len(vertices) * 4, (GLfloat * len(vertices))(*vertices), GL_STATIC_DRAW ) glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0 ) glEnableVertexAttribArray( 0 ) # create a VBO for our texture coordinates glBindBuffer( GL_ARRAY_BUFFER, self.vbo[ 1 ] ) glBufferData( GL_ARRAY_BUFFER, len(texture_coords) * 4, (GLfloat * len(texture_coords))(*texture_coords), GL_STATIC_DRAW ) glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, 0 ) glEnableVertexAttribArray( 1 ) # create a VBO for our normals glBindBuffer( GL_ARRAY_BUFFER, self.vbo[ 2 ] ) glBufferData( GL_ARRAY_BUFFER, len(normals) * 4, (GLfloat * len(normals))(*normals), GL_STATIC_DRAW ) glVertexAttribPointer( 2, 3, GL_FLOAT, GL_FALSE, 0, 0 ) glEnableVertexAttribArray( 2 ) # unbind our buffers glBindBuffer( GL_ARRAY_BUFFER, 0 ) glBindVertexArray( 0 ) def render( self, projection, model_view, groups ): self.shader.bind() self.shader.uniforms.in_model_view = model_view self.shader.uniforms.in_projection = projection glBindVertexArray( self.vao ) # iterate through the specified groups for group in groups: # get the group for mesh in self.meshes[ group ]: element_vbo, points, lines, faces = mesh # render the group glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, element_vbo ) if points[ 1 ] > 0: glDrawElements( GL_POINTS, points[ 1 ], GL_UNSIGNED_INT, 0 ) if lines[ 1 ] > 0: glDrawElements( GL_LINES, lines[ 1 ], GL_UNSIGNED_INT, points[ 1 ] ) if faces[ 1 ] > 0: glDrawElements( GL_TRIANGLES, faces[ 1 ], GL_UNSIGNED_INT, points[ 1 ] + lines[ 1 ] ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ) glBindVertexArray( 0 ) self.shader.unbind()
class Data( object ): """ Provides the ability to load and render an MD2 mesh. Uses MD2 to load MD2 mesh data. Loads mesh data onto the graphics card to speed up rendering. Allows for pre-baking the interpolation of frames. """ shader_source = { 'vert': open(os.path.dirname(__file__) + '/md2.vert','r').read(), 'frag': open(os.path.dirname(__file__) + '/md2.frag','r').read(), } _data = {} @classmethod def load( cls, filename ): # check if the model has been loaded previously if filename in Data._data: # create a new mesh with the same data return Data._data[ filename ] data = cls( filename ) # store mesh for later Data._data[ filename ] = data return data @classmethod def unload( cls, filename ): if filename in Data._data: del Data._data[ filename ] def __init__( self, filename = None, buffer = None ): """ Loads an MD2 from the specified file. @param filename: the filename to load the mesh from. @param interpolation: the number of frames to generate between each loaded frame. 0 is the default (no interpolation). It is suggested to keep the value low (0-2) to avoid long load times. """ super( Data, self ).__init__() self.frames = None self.vao = None self.tc_vbo = None self.indice_vbo = None self.shader = ShaderProgram( Shader( GL_VERTEX_SHADER, Data.shader_source['vert'] ), Shader( GL_FRAGMENT_SHADER, Data.shader_source['frag'] ), link_now = False ) # set our shader data # we MUST do this before we link the shader self.shader.attributes.in_position_1 = 0 self.shader.attributes.in_normal_1 = 1 self.shader.attributes.in_position_2 = 2 self.shader.attributes.in_normal_2 = 3 self.shader.attributes.in_texture_coord = 4 self.shader.frag_location( 'out_frag_colour' ) # link the shader now self.shader.link() # bind our uniform indices self.shader.bind() self.shader.uniforms.in_diffuse = 0 self.shader.unbind() self.md2 = pymesh.md2.MD2() if filename != None: self.md2.load( filename ) else: self.md2.load_from_buffer( buffer ) # load into OpenGL self._load() def __del__( self ): # free our vao vao = getattr( self, 'vao', None ) if vao: glDeleteVertexArrays( 1, vao ) # free our vbos # texture coords tcs = getattr( self, 'tc_vbo', None ) if tcs: glDeleteBuffer( tcs ) # indices indices = getattr( self, 'indice_vbo', None ) if indices: glDeleteBuffer( indices ) # frames frames = getattr( self, 'frames', None ) if frames: for frame in frames: glDeleteBuffer( frame ) def _load( self ): """ Prepares the MD2 for rendering by OpenGL. """ def process_vertices( md2 ): """Processes MD2 data to generate a single set of indices. MD2 is an older format that has 2 sets of indices. Vertex/Normal indices (md2.triangles.vertex_indices) and Texture Coordinate indices (md2.triangles.tc_indices). The problem is that modern 3D APIs don't like this. OpenGL only allows a single set of indices. We can either, extract the vertices, normals and texture coordinates using the indices. This will create a lot of data. This function provides an alternative. We iterate through the indices and determine if an index has a unique vertex/normal and texture coordinate value. If so, the index remains and the texture coordinate is moved into the vertex index location in the texture coordinate array. If not, a new vertex/normal/texture coordinate value is created and the index is updated. This function returns a tuple containing the following values. ( [ new indices ], [ new texture coordinate array ], [ frame_layout( name, vertices, normals ) ] ) """ # convert our vertex / tc indices to a single indice # we iterate through our list and indices = [] frames = [ ( frame.name, list(frame.vertices), list(frame.normals) ) for frame in md2.frames ] # set the size of our texture coordinate list to the # same size as one of our frame's vertex lists tcs = list( [[None, None]] * len(frames[ 0 ][ 1 ]) ) for v_index, tc_index in zip( md2.triangles.vertex_indices, md2.triangles.tc_indices, ): indice = v_index if \ tcs[ v_index ][ 0 ] == None and \ tcs[ v_index ][ 1 ] == None: # no tc set yet # set ours tcs[ v_index ][ 0 ] = md2.tcs[ tc_index ][ 0 ] tcs[ v_index ][ 1 ] = md2.tcs[ tc_index ][ 1 ] elif \ tcs[ v_index ][ 0 ] != md2.tcs[ tc_index ][ 0 ] and \ tcs[ v_index ][ 1 ] != md2.tcs[ tc_index ][ 1 ]: # a tc has been set and it's not ours # create a new indice indice = len( tcs ) # add a new unique vertice for frame in frames: # vertex data frame[ 1 ].append( frame[ 1 ][ v_index ] ) # normal data frame[ 2 ].append( frame[ 2 ][ v_index ] ) # texture coordinate tcs.append( [ md2.tcs[ tc_index ][ 0 ], md2.tcs[ tc_index ][ 1 ] ] ) # store the index indices.append( indice ) # convert our frames to frame tuples frame_tuples = [ pymesh.md2.MD2.frame_layout( frame[ 0 ], numpy.array( frame[ 1 ], dtype = numpy.float ), numpy.array( frame[ 2 ], dtype = numpy.float ) ) for frame in frames ] return ( numpy.array( indices ), numpy.array( tcs ), frame_tuples ) indices, tcs, frames = process_vertices( self.md2 ) self.num_indices = len( indices ) # create a vertex array object # and vertex buffer objects for our core data self.vao = (GLuint)() glGenVertexArrays( 1, self.vao ) # load our buffers glBindVertexArray( self.vao ) # create our vbo buffers # one for texture coordinates # one for indices vbos = (GLuint * 2)() glGenBuffers( len(vbos), vbos ) self.tc_vbo = vbos[ 0 ] self.indice_vbo = vbos[ 1 ] # create our texture coordintes tcs = tcs.astype( 'float32' ) glBindBuffer( GL_ARRAY_BUFFER, self.tc_vbo ) glBufferData( GL_ARRAY_BUFFER, tcs.nbytes, (GLfloat * tcs.size)(*tcs.flat), GL_STATIC_DRAW ) # create our index buffer indices = indices.astype( 'uint32' ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, self.indice_vbo ) glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, (GLuint * indices.size)(*indices.flat), GL_STATIC_DRAW ) def create_frame_data( vertices, normals ): vbo = (GLuint)() glGenBuffers( 1, vbo ) # interleave these arrays into a single array array = numpy.empty( (len(vertices) * 2, 3), dtype = 'float32' ) array[::2] = vertices array[1::2] = normals glBindBuffer( GL_ARRAY_BUFFER, vbo ) glBufferData( GL_ARRAY_BUFFER, array.nbytes, (GLfloat * array.size)(*array.flat), GL_STATIC_DRAW ) return vbo # convert our frame data into VBOs self.frames = [ create_frame_data( frame.vertices, frame.normals ) for frame in frames ] # unbind our buffers glBindVertexArray( 0 ) glBindBuffer( GL_ARRAY_BUFFER, 0 ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ) @property def num_frames( self ): return len( self.md2.frames ) def render( self, frame1, frame2, interpolation, projection, model_view ): # bind our shader and pass in our model view self.shader.bind() self.shader.uniforms.in_model_view = model_view self.shader.uniforms.in_projection = projection self.shader.uniforms.in_fraction = interpolation # we don't bind the diffuse texture # this is up to the caller to allow # multiple textures to be used per mesh instance frame1_data = self.frames[ frame1 ] frame2_data = self.frames[ frame2 ] # unbind the shader glBindVertexArray( self.vao ) vertex_size = 6 * 4 vertex_offset = 0 * 4 normal_offset = 3 * 4 # frame 1 glBindBuffer( GL_ARRAY_BUFFER, frame1_data ) glEnableVertexAttribArray( 0 ) glEnableVertexAttribArray( 1 ) glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, vertex_size, vertex_offset ) glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, vertex_size, normal_offset ) # frame 2 glBindBuffer( GL_ARRAY_BUFFER, frame2_data ) glEnableVertexAttribArray( 2 ) glEnableVertexAttribArray( 3 ) glVertexAttribPointer( 2, 3, GL_FLOAT, GL_FALSE, vertex_size, vertex_offset ) glVertexAttribPointer( 3, 3, GL_FLOAT, GL_FALSE, vertex_size, normal_offset ) # texture coords glBindBuffer( GL_ARRAY_BUFFER, self.tc_vbo ) glEnableVertexAttribArray( 4 ) glVertexAttribPointer( 4, 2, GL_FLOAT, GL_FALSE, 0, 0 ) # indices glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, self.indice_vbo ) glDrawElements( GL_TRIANGLES, self.num_indices, GL_UNSIGNED_INT, 0 ) # reset our state glBindVertexArray( 0 ) glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ) glBindBuffer( GL_ARRAY_BUFFER, 0 ) self.shader.unbind()
class LegacyQuad( object ): vertex_shader = textwrap.dedent( """ #version 120 void main(void) { // apply projection and model view matrix to vertex gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; // select the texture coordinate to use gl_TexCoord[0] = gl_MultiTexCoord0; } """ ) fragment_shader = textwrap.dedent( """ #version 120 // input uniform sampler2D in_diffuse_texture; void main(void) { // set colour of each fragment gl_FragColor = texture2D( in_diffuse_texture, gl_TexCoord[0].st ); } """ ) def __init__( self ): super( LegacyQuad, self ).__init__() global vertices self.use_shaders = True GL.glEnable(GL.GL_TEXTURE_2D) # create our shader self.shader = ShaderProgram( VertexShader( self.vertex_shader ), FragmentShader( self.fragment_shader ) ) # create our vertex buffer self.buffer = VertexBuffer( GL.GL_ARRAY_BUFFER, GL.GL_STATIC_DRAW, data = vertices, ) self.buffer_attributes = BufferAttributes() self.buffer_attributes[ 'position' ] = VertexAttribute.from_dtype( self.buffer, vertices.dtype, 'position' ) self.buffer_attributes[ 'uv' ] = TextureCoordAttribute.from_dtype( self.buffer, vertices.dtype, 'texture_coord' ) def draw( self ): global vertices if self.use_shaders: self.shader.bind() self.shader.uniforms[ 'in_diffuse_texture' ].value = 0 self.buffer_attributes.push_attributes() # set the vertex pointer to the position data self.buffer.bind() self.buffer_attributes.set() self.buffer.unbind() GL.glDrawArrays( GL.GL_TRIANGLES, 0, len( vertices ) ) self.buffer_attributes.pop_attributes() if self.use_shaders: self.shader.unbind()
class Data(object): shader_source = { 'vert': open(os.path.dirname(__file__) + '/obj.vert', 'r').read(), 'frag': open(os.path.dirname(__file__) + '/obj.frag', 'r').read() } _data = {} @classmethod def load(cls, filename): # check if the model has been loaded previously if filename in Data._data: # create a new mesh with the same data return Data._data[filename] data = cls(filename) # store mesh for later Data._data[filename] = data return data @classmethod def unload(cls, filename): if filename in Data._data: del Data._data[filename] def __init__(self, filename=None, buffer=None): super(Data, self).__init__() self.meshes = {} # create our shader self.shader = ShaderProgram(Shader(GL_VERTEX_SHADER, Data.shader_source['vert']), Shader(GL_FRAGMENT_SHADER, Data.shader_source['frag']), link_now=False) # set our shader data # we MUST do this before we link the shader self.shader.attributes.in_position = 0 self.shader.attributes.in_texture_coord = 1 self.shader.attributes.in_normal = 2 self.shader.frag_location('out_frag_colour') # link the shader now self.shader.link() # bind our uniform indices self.shader.bind() self.shader.uniforms.tex0 = 0 self.shader.unbind() self.obj = pymesh.obj.OBJ() if filename != None: self.obj.load(filename) else: self.obj.load_from_buffer(buffer) self._load() def _load(self): """ Processes the data loaded by the MD2 Loader """ # convert the md2 data into data for the gpu # first, load our vertex buffer objects self._load_vertex_buffers() def _load_vertex_buffers(self): # we need to convert from 3 lists with 3 sets of indices # to 3 lists with 1 set of indices # so for each index, we need to check if we already # have a matching vertex, and if not, make one vertex_bin = OrderedDict([]) vertices = [] texture_coords = [] normals = [] def process_vertex_data(bin, vertices, texture_coords, normals, data): # check if we've already got this unique vertex in our list if data not in bin: # the vertex doesn't exist yet # insert into our vertex bin bin[data] = len(bin) # convert our indices into actual data v_index, tc_index, n_index = data vertices.extend(list(self.obj.model.vertices[v_index])) # map our texture coordinates # if no tc is present, insert 0.0, 0.0 if tc_index != None: texture_coords.extend( list(self.obj.model.texture_coords[tc_index])) else: texture_coords.extend([0.0, 0.0]) # map our normals # if no normal is present, insert 0.0, 0.0, 0.0 if n_index != None: normals.extend(list(self.obj.model.normals[n_index])) else: normals.extend([0.0, 0.0, 0.0]) # return the new index return bin[data] for mesh in self.obj.model.meshes: indices = [] num_points = 0 num_lines = 0 num_faces = 0 # check if we need to create a point mesh if len(mesh['points']) > 0: # remap each point from a random set of indices # to a unique vertex for point in mesh['points']: indices.append( process_vertex_data(vertex_bin, vertices, texture_coords, normals, point)) num_points = len(mesh['points']) # check if we need to create a line mesh if len(mesh['lines']) > 0: # each line tuple is a line strip # the easiest way to render is to convert to # line segments def convert_to_lines(strip): result = [] previous = strip[0] for point in strip[1:]: result.extend([previous, point]) previous = point return result # convert each line strip into line segments line_segments = [] for strip in mesh['lines']: line_segments.extend(convert_to_lines(strip)) # remap each point from a random set of indices # to a unique vertex for point in line_segments: indices.append( process_vertex_data(vertex_bin, vertices, texture_coords, normals, point)) num_lines = len(line_segments) # check if we need to create a face mesh if len(mesh['faces']) > 0: # faces are stored as a list of triangle fans # we need to covnert them to triangles def convert_to_triangles(fan): # convert from triangle fan # 0, 1, 2, 3, 4, 5 # to triangle list # 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5 result = [] start = fan[0] previous = fan[1] for point in fan[2:]: result.extend([start, previous, point]) previous = point return result # convert each triangle face to triangles triangle_indices = [] for face in mesh['faces']: triangle_indices.extend(convert_to_triangles(face)) # remap each point from a random set of indices # to a unique vertex for point in triangle_indices: indices.append( process_vertex_data(vertex_bin, vertices, texture_coords, normals, point)) num_faces = len(triangle_indices) # create our index arrays element_vbo = (GLuint)() glGenBuffers(1, element_vbo) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_vbo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(indices) * 4, (GLuint * len(indices))(*indices), GL_STATIC_DRAW) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) # store our indices gl_data = (element_vbo, (0, num_points), (num_points, num_lines), (num_points + num_lines, num_faces)) # add the mesh to each of the mesh groups # each group has a list of meshes it owns for group in mesh['groups']: if group not in self.meshes: self.meshes[group] = [] self.meshes[group].append(gl_data) self.vao = (GLuint)() glGenVertexArrays(1, self.vao) glBindVertexArray(self.vao) # create our global vertex data self.vbo = (GLuint * 3)() glGenBuffers(3, self.vbo) # create a VBO for our vertices glBindBuffer(GL_ARRAY_BUFFER, self.vbo[0]) glBufferData(GL_ARRAY_BUFFER, len(vertices) * 4, (GLfloat * len(vertices))(*vertices), GL_STATIC_DRAW) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0) glEnableVertexAttribArray(0) # create a VBO for our texture coordinates glBindBuffer(GL_ARRAY_BUFFER, self.vbo[1]) glBufferData(GL_ARRAY_BUFFER, len(texture_coords) * 4, (GLfloat * len(texture_coords))(*texture_coords), GL_STATIC_DRAW) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0) glEnableVertexAttribArray(1) # create a VBO for our normals glBindBuffer(GL_ARRAY_BUFFER, self.vbo[2]) glBufferData(GL_ARRAY_BUFFER, len(normals) * 4, (GLfloat * len(normals))(*normals), GL_STATIC_DRAW) glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0) glEnableVertexAttribArray(2) # unbind our buffers glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) def render(self, projection, model_view, groups): self.shader.bind() self.shader.uniforms.in_model_view = model_view self.shader.uniforms.in_projection = projection glBindVertexArray(self.vao) # iterate through the specified groups for group in groups: # get the group for mesh in self.meshes[group]: element_vbo, points, lines, faces = mesh # render the group glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_vbo) if points[1] > 0: glDrawElements(GL_POINTS, points[1], GL_UNSIGNED_INT, 0) if lines[1] > 0: glDrawElements(GL_LINES, lines[1], GL_UNSIGNED_INT, points[1]) if faces[1] > 0: glDrawElements(GL_TRIANGLES, faces[1], GL_UNSIGNED_INT, points[1] + lines[1]) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) glBindVertexArray(0) self.shader.unbind()