def __init__( self ):
        super( LegacyColourCube, 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'
            )
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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 __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'
            )
Exemplo n.º 5
0
    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'
            )
Exemplo n.º 6
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()
Exemplo n.º 7
0
    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 __init__( self ):
        super( CoreQuad, 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[ 'uv' ] = GenericAttribute.from_dtype(
            self.buffer,
            vertices.dtype,
            'texture_coord',
            location = self.shader.attributes[ 'in_uv' ]
            )

        # create our vertex array
        self.vao = VertexArray()

        self.vao.bind()
        self.buffer.bind()
        self.buffer_attributes.set()
        self.buffer.unbind()
        self.vao.unbind()
Exemplo n.º 9
0
    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()
Exemplo n.º 10
0
    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()
Exemplo n.º 11
0
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()
Exemplo n.º 12
0
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()
Exemplo n.º 13
0
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()
Exemplo n.º 14
0
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()
Exemplo n.º 16
0
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()
Exemplo n.º 17
0
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()
Exemplo n.º 18
0
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()
Exemplo n.º 19
0
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()
Exemplo n.º 20
0
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()