Exemple #1
0
    def __init__(self, dtype):
        # Parse vertices dtype and generate attributes
        gltypes = { 'float32': gl.GL_FLOAT,
                    'float'  : gl.GL_DOUBLE, 'float64': gl.GL_DOUBLE,
                    'int8'   : gl.GL_BYTE,   'uint8'  : gl.GL_UNSIGNED_BYTE,
                    'int16'  : gl.GL_SHORT,  'uint16' : gl.GL_UNSIGNED_SHORT,
                    'int32'  : gl.GL_INT,    'uint32' : gl.GL_UNSIGNED_INT }
        dtype = np.dtype(dtype)
        names = dtype.names or []
        stride = dtype.itemsize
        offset = 0
        self._attributes = []
        for i,name in enumerate(names):
            if dtype[name].subdtype is not None:
                gtype = str(dtype[name].subdtype[0])
                count = reduce(lambda x,y:x*y, dtype[name].shape)
            else:
                gtype = str(dtype[name])
                count = 1
            if gtype not in gltypes.keys():
                raise VertexBufferException('Data type not understood')
            gltype = gltypes[gtype]
            attribute = VertexAttribute(name,count,gltype,stride,offset)
            self._attributes.append( attribute )
            offset += dtype[name].itemsize

        self._vertices = DynamicBuffer(dtype)
        self._indices  = DynamicBuffer(np.uint32)
        self._vertices_id = 0
        self._indices_id = 0
        self._dirty = True
Exemple #2
0
    def __init__(self, vtype, utype):
        self.dash_atlas = None

        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append(('a_index', 'f4'))

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError(
                "Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            size = count // 4
            if count % 4:
                size += 1
                utype.append(('unnused', 'f4', size * 4 - count))
            count = size * 4
            self.utype = utype
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer(utype)
        self._ubuffer_id = 0
        self._ubuffer_shape = [0, count]

        self._dirty = True
Exemple #3
0
    def __init__(self, vtype, utype):
        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append( ('a_index', 'f4') )

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError("Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            count2 = int(math.pow(2, math.ceil(math.log(count, 2))))
            if (count2 - count) > 0:
                utype.append(('unused', 'f4', count2-count))
            self._count = count2
            self.utype = utype
        
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer( utype )
        self._ubuffer_id = 0
        self._max_texture_size = gl.glGetInteger(gl.GL_MAX_TEXTURE_SIZE)
        self._compute_ushape(1)
        self._ubuffer.reserve( self._ushape[1] / (count/4) )
        self._dirty = True
Exemple #4
0
    def __init__(self, vtype, utype):
        self.dash_atlas = None

        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append( ('a_index', 'f4') )

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError("Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            size = count//4
            if count % 4:
                size += 1
                utype.append(('unnused', 'f4', size*4-count))
            count = size*4
            self.utype = utype
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer( utype )
        self._ubuffer_id = 0
        self._ubuffer_shape = [0,count]

        self._dirty = True
Exemple #5
0
 def __init__(self, dash_atlas = None):
     self.vtype = np.dtype( [('a_texcoord', 'f4', 2)] )
     self.utype = np.dtype( [('translate',        'f4', 2),
                             ('scale',            'f4', 1),
                             ('rotate',           'f4', 1),
                             ('major_grid',       'f4', 2),
                             ('minor_grid',       'f4', 2),
                             ('major_tick_size',  'f4', 2),
                             ('minor_tick_size',  'f4', 2),
                             ('major_grid_color', 'f4', 4),
                             ('minor_grid_color', 'f4', 4),
                             ('major_tick_color', 'f4', 4),
                             ('minor_tick_color', 'f4', 4),
                             ('major_grid_width', 'f4', 1),
                             ('minor_grid_width', 'f4', 1),
                             ('major_tick_width', 'f4', 1),
                             ('minor_tick_width', 'f4', 1),
                             ('size',             'f4', 2),
                             ('offset',           'f4', 2),
                             ('zoom',             'f4', 1),
                             ('antialias',        'f4', 1),
                             ('major_dash_phase', 'f4', 1),
                             ('minor_dash_phase', 'f4', 1),
                             ('major_dash_index', 'f4', 1),
                             ('major_dash_period','f4', 1),
                             ('major_dash_caps',  'f4', 2),
                             ('minor_dash_period','f4', 1),
                             ('minor_dash_index', 'f4', 1),
                             ('minor_dash_caps',  'f4', 2)
                             ] )
     self.gtype = np.dtype( [('name', 'f4', (1024,4))] )
     Collection.__init__(self, self.vtype, self.utype)
     if dash_atlas is None:
         self.dash_atlas = DashAtlas()
     else:
         self.dash_atlas = dash_atlas
     shaders = os.path.join(os.path.dirname(__file__),'shaders')
     vertex_shader= os.path.join( shaders, 'grid.vert')
     fragment_shader= os.path.join( shaders, 'grid.frag')
     self.shader = Shader( open(vertex_shader).read(),
                           open(fragment_shader).read() )
     self._gbuffer = DynamicBuffer( self.gtype )
     self._gbuffer_shape = [0,4*1024]
     self._gbuffer_id = 0
Exemple #6
0
class VertexBuffer(object):

    # ---------------------------------
    def __init__(self, dtype):
        # Parse vertices dtype and generate attributes
        gltypes = { 'float32': gl.GL_FLOAT,
                    'float'  : gl.GL_DOUBLE, 'float64': gl.GL_DOUBLE,
                    'int8'   : gl.GL_BYTE,   'uint8'  : gl.GL_UNSIGNED_BYTE,
                    'int16'  : gl.GL_SHORT,  'uint16' : gl.GL_UNSIGNED_SHORT,
                    'int32'  : gl.GL_INT,    'uint32' : gl.GL_UNSIGNED_INT }
        dtype = np.dtype(dtype)
        names = dtype.names or []
        stride = dtype.itemsize
        offset = 0
        self._attributes = []
        for i,name in enumerate(names):
            if dtype[name].subdtype is not None:
                gtype = str(dtype[name].subdtype[0])
                count = reduce(lambda x,y:x*y, dtype[name].shape)
            else:
                gtype = str(dtype[name])
                count = 1
            if gtype not in gltypes.keys():
                raise VertexBufferException('Data type not understood')
            gltype = gltypes[gtype]
            attribute = VertexAttribute(name,count,gltype,stride,offset)
            self._attributes.append( attribute )
            offset += dtype[name].itemsize

        self._vertices = DynamicBuffer(dtype)
        self._indices  = DynamicBuffer(np.uint32)
        self._vertices_id = 0
        self._indices_id = 0
        self._dirty = True


    # ---------------------------------
    def get_vertices(self):
        return self._vertices
    vertices = property(get_vertices)


    # ---------------------------------
    def get_indices(self):
        return self._indices
    indices = property(get_indices)


    # ---------------------------------
    def clear(self):
        self._vertices.clear()
        self._indices.clear()
        self._dirty = True


    # ---------------------------------
    def append(self, vertices, indices):
        vsize = len(self._vertices.data)
        vertices = np.array(vertices).astype(self._vertices.dtype)
        self._vertices.append(vertices)
        indices = np.array(indices).astype(self._indices.dtype) + vsize
        self._indices.append(indices)
        self._dirty = True


    # ---------------------------------
    def __delitem__(self, key):
        vsize = len(self._vertices[key])
        _,_,dstart,_ = self._indices._get_indices(key)
        del self._vertices[key]
        del self._indices[key]
        self._indices.data[dstart:] -= vsize
        self._dirty = True


    # ---------------------------------
    def __getitem__(self, key):
        return self._vertices[key], self._indices[key]


    # ---------------------------------
    def __len__(self):
        return len(self.vertices)


    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return

        if not self._vertices_id:
            self._vertices_id = gl.glGenBuffers(1)
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, self._vertices_id )
        gl.glBufferData( gl.GL_ARRAY_BUFFER, self._vertices.data, gl.GL_DYNAMIC_DRAW )
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 )

        if not self._indices_id:
            self._indices_id = gl.glGenBuffers(1)
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id )
        gl.glBufferData( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices.data, gl.GL_DYNAMIC_DRAW )
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, 0 )

        self._dirty = False


    # ---------------------------------
    def draw( self, mode=gl.GL_TRIANGLES ):

        if self._dirty:
            self.upload()
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, self._vertices_id )
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id )
        for attribute in self._attributes:
            attribute.enable()
        gl.glDrawElements( mode, len(self._indices.data), gl.GL_UNSIGNED_INT, None)
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, 0 )
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 )
Exemple #7
0
class Collection(object):

    join = {'miter': 0, 'round': 1, 'bevel': 2}

    caps = {
        '': 0,
        'none': 0,
        '.': 0,
        'round': 1,
        ')': 1,
        '(': 1,
        'o': 1,
        'triangle in': 2,
        '<': 2,
        'triangle out': 3,
        '>': 3,
        'square': 4,
        '=': 4,
        'butt': 4,
        '|': 5
    }

    # ---------------------------------
    def __init__(self, vtype, utype):
        self.dash_atlas = None

        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append(('a_index', 'f4'))

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError(
                "Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            size = count // 4
            if count % 4:
                size += 1
                utype.append(('unnused', 'f4', size * 4 - count))
            count = size * 4
            self.utype = utype
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer(utype)
        self._ubuffer_id = 0
        self._ubuffer_shape = [0, count]

        self._dirty = True

    # ---------------------------------
    def __len__(self):
        return len(self._vbuffer)

    # ---------------------------------
    def __getitem__(self, key):
        V = self._vbuffer.vertices[key]
        I = self._vbuffer.indices[key]
        U = self._ubuffer[key]
        return Item(self, key, V, I, U)

    # ---------------------------------
    def __delitem__(self, key):
        start, end = self._vbuffer.vertices.range(key)
        del self._vbuffer[key]
        del self._ubuffer[key]
        self._vbuffer.vertices.data['a_index'][start:] -= 1
        self._vbuffer._dirty = True
        self._dirty = True

    # ---------------------------------
    def get_vertices(self):
        return self._vbuffer.vertices

    vertices = property(get_vertices)

    # ---------------------------------
    def get_indices(self):
        return self._vbuffer.indices

    indices = property(get_indices)

    # ---------------------------------
    def get_uniforms(self):
        return self._ubuffer

    uniforms = property(get_uniforms)

    # ---------------------------------
    def __getattr__(self, name):
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self, '_ubuffer')
            if name in buffer.dtype.names:
                return buffer.data[name]
        return object.__getattribute__(self, name)

    # ---------------------------------
    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self, '_ubuffer')
            if name in buffer.dtype.names:
                buffer.data[name] = value
                # buffer._dirty = True
                object.__setattr__(self, '_dirty', True)
        object.__setattr__(self, name, value)

    # ---------------------------------
    def clear(self):
        self._vbuffer.clear()
        self._ubuffer.clear()
        self._dirty = True

    # ---------------------------------
    def append(self, vertices, indices, uniforms):
        vertices = np.array(vertices).astype(self._vbuffer.vertices.dtype)
        indices = np.array(indices).astype(self._vbuffer.indices.dtype)
        uniforms = np.array(uniforms).astype(self._ubuffer.dtype)
        vertices['a_index'] = len(self)
        self._vbuffer.append(vertices, indices)
        self._ubuffer.append(uniforms)
        self._ubuffer_shape[0] = len(self)
        self._dirty = True

    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return

        self._vbuffer.upload()

        gl.glActiveTexture(gl.GL_TEXTURE0)
        data = self._ubuffer.data.view(np.float32)
        shape = self._ubuffer_shape
        if not self._ubuffer_id:
            self._ubuffer_id = gl.glGenTextures(1)

            gl.glBindTexture(gl.GL_TEXTURE_2D, self._ubuffer_id)
            gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
            gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER,
                               gl.GL_NEAREST)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER,
                               gl.GL_NEAREST)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S,
                               gl.GL_CLAMP_TO_EDGE)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T,
                               gl.GL_CLAMP_TO_EDGE)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_BASE_LEVEL, 0)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0)
            gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F, shape[1] // 4,
                            shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data)

        gl.glActiveTexture(gl.GL_TEXTURE0)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._ubuffer_id)
        gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F, shape[1] // 4,
                        shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data)
        #gl.glTexSubImage2D( gl.GL_TEXTURE_2D,0, 0, 0, shape[1]//4, shape[0],
        #                    gl.GL_RGBA, gl.GL_FLOAT, data);
        self._dirty = False

    # ---------------------------------
    def bake(self, vertices):
        raise NotImplemented

    # ---------------------------------
    def draw(self):
        if self._dirty:
            self.upload()

        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glEnable(gl.GL_BLEND)
        _, _, width, height = gl.glGetIntegerv(gl.GL_VIEWPORT)
        P = orthographic(0, width, 0, height, -1, +1)
        V = np.eye(4).astype(np.float32)
        M = np.eye(4).astype(np.float32)

        shader = self.shader
        shader.bind()
        gl.glActiveTexture(gl.GL_TEXTURE0)
        shader.uniformi('u_uniforms', 0)
        gl.glBindTexture(gl.GL_TEXTURE_2D, self._ubuffer_id)
        if self.dash_atlas:
            gl.glActiveTexture(gl.GL_TEXTURE1)
            shader.uniformi('u_dash_atlas', 1)
            gl.glBindTexture(gl.GL_TEXTURE_2D, self.dash_atlas.texture_id)
        shader.uniform_matrixf('u_M', M)
        shader.uniform_matrixf('u_V', V)
        shader.uniform_matrixf('u_P', P)
        shape = self._ubuffer_shape
        shader.uniformf('u_uniforms_shape', shape[1] // 4, shape[0])
        self._vbuffer.draw()
        shader.unbind()
Exemple #8
0
class Collection(object):

    join = { 'miter' : 0,
             'round' : 1,
             'bevel' : 2 }

    caps = { ''             : 0,
             'none'         : 0,
             '.'            : 0,
             'round'        : 1,
             ')'            : 1,
             '('            : 1,
             'o'            : 1,
             'triangle in'  : 2,
             '<'            : 2,
             'triangle out' : 3,
             '>'            : 3,
             'square'       : 4,
             '='            : 4,
             'butt'         : 4,
             '|'            : 5 }


    # ---------------------------------
    def __init__(self, vtype, utype):
        self.dash_atlas = None

        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append( ('a_index', 'f4') )

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError("Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            size = count//4
            if count % 4:
                size += 1
                utype.append(('unnused', 'f4', size*4-count))
            count = size*4
            self.utype = utype
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer( utype )
        self._ubuffer_id = 0
        self._ubuffer_shape = [0,count]

        self._dirty = True

    # ---------------------------------
    def __len__(self):
        return len(self._vbuffer)


    # ---------------------------------
    def __getitem__(self, key):
        V = self._vbuffer.vertices[key]
        I = self._vbuffer.indices[key]
        U = self._ubuffer[key]
        return Item(self, key, V, I, U)


    # ---------------------------------
    def __delitem__(self, key):
        start,end = self._vbuffer.vertices.range(key)
        del self._vbuffer[key]
        del self._ubuffer[key]
        self._vbuffer.vertices.data['a_index'][start:] -= 1
        self._vbuffer._dirty = True
        self._dirty = True


    # ---------------------------------
    def get_vertices(self):
        return self._vbuffer.vertices
    vertices = property(get_vertices)


    # ---------------------------------
    def get_indices(self):
        return self._vbuffer.indices
    indices = property(get_indices)


    # ---------------------------------
    def get_uniforms(self):
        return self._ubuffer
    uniforms = property(get_uniforms)


    # ---------------------------------
    def __getattr__(self, name):
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self,'_ubuffer')
            if name in buffer.dtype.names:
                return buffer.data[name]
        return object.__getattribute__(self,name)


    # ---------------------------------
    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self,'_ubuffer')
            if name in buffer.dtype.names:
                buffer.data[name] = value
                # buffer._dirty = True
                object.__setattr__(self, '_dirty', True)
        object.__setattr__(self, name, value)


    # ---------------------------------
    def clear(self):
        self._vbuffer.clear()
        self._ubuffer.clear()
        self._dirty = True


    # ---------------------------------
    def append(self, vertices, indices, uniforms):
        vertices = np.array(vertices).astype(self._vbuffer.vertices.dtype)
        indices  = np.array(indices).astype(self._vbuffer.indices.dtype)
        uniforms = np.array(uniforms).astype(self._ubuffer.dtype)
        vertices['a_index'] = len(self)
        self._vbuffer.append( vertices, indices)
        self._ubuffer.append( uniforms )
        self._ubuffer_shape[0] = len(self)
        self._dirty = True


    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return

        self._vbuffer.upload()

        gl.glActiveTexture( gl.GL_TEXTURE0 )
        data = self._ubuffer.data.view(np.float32)
        shape = self._ubuffer_shape
        if not self._ubuffer_id:
            self._ubuffer_id = gl.glGenTextures(1)

            gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
            gl.glPixelStorei( gl.GL_UNPACK_ALIGNMENT, 1 )
            gl.glPixelStorei( gl.GL_PACK_ALIGNMENT, 1 )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_BASE_LEVEL, 0)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0)
            gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                             shape[1]//4, shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )

        gl.glActiveTexture( gl.GL_TEXTURE0 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
        gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                         shape[1]//4, shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )
        #gl.glTexSubImage2D( gl.GL_TEXTURE_2D,0, 0, 0, shape[1]//4, shape[0],
        #                    gl.GL_RGBA, gl.GL_FLOAT, data);
        self._dirty = False


    # ---------------------------------
    def bake(self, vertices):
        raise NotImplemented


    # ---------------------------------
    def draw(self):
        if self._dirty:
            self.upload()

        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glEnable(gl.GL_BLEND)
        _,_,width,height = gl.glGetIntegerv( gl.GL_VIEWPORT )
        P = orthographic( 0, width, 0, height, -1, +1 )
        V = np.eye(4).astype( np.float32 )
        M = np.eye(4).astype( np.float32 )

        shader = self.shader
        shader.bind()
        gl.glActiveTexture( gl.GL_TEXTURE0 )
        shader.uniformi( 'u_uniforms', 0 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
        if self.dash_atlas:
            gl.glActiveTexture( gl.GL_TEXTURE1 )
            shader.uniformi('u_dash_atlas', 1)
            gl.glBindTexture( gl.GL_TEXTURE_2D, self.dash_atlas.texture_id )
        shader.uniform_matrixf( 'u_M', M )
        shader.uniform_matrixf( 'u_V', V )
        shader.uniform_matrixf( 'u_P', P )
        shape = self._ubuffer_shape
        shader.uniformf( 'u_uniforms_shape', shape[1]//4, shape[0])
        self._vbuffer.draw( )
        shader.unbind()
class VertexBuffer(object):

    # ---------------------------------
    def __init__(self, dtype):
        # Parse vertices dtype and generate attributes
        gltypes = { 'float32': gl.GL_FLOAT,
                    'float'  : gl.GL_DOUBLE, 'float64': gl.GL_DOUBLE,
                    'int8'   : gl.GL_BYTE,   'uint8'  : gl.GL_UNSIGNED_BYTE,
                    'int16'  : gl.GL_SHORT,  'uint16' : gl.GL_UNSIGNED_SHORT,
                    'int32'  : gl.GL_INT,    'uint32' : gl.GL_UNSIGNED_INT }
        dtype = np.dtype(dtype)
        names = dtype.names or []
        stride = dtype.itemsize
        offset = 0
        self._attributes = []
        for i,name in enumerate(names):
            if dtype[name].subdtype is not None:
                gtype = str(dtype[name].subdtype[0])
                count = reduce(lambda x,y:x*y, dtype[name].shape)
            else:
                gtype = str(dtype[name])
                count = 1
            if gtype not in gltypes.keys():
                raise VertexBufferException('Data type not understood')
            gltype = gltypes[gtype]
            attribute = VertexAttribute(name,count,gltype,stride,offset)
            self._attributes.append( attribute )
            offset += dtype[name].itemsize

        self._dsize = offset
        self._vertices = DynamicBuffer(dtype)
        self._indices  = DynamicBuffer(np.uint32)
        self._vertices_id = 0
        self._indices_id = 0
        self._dirty = True


    # ---------------------------------
    def get_vertices(self):
        return self._vertices
    vertices = property(get_vertices)


    # ---------------------------------
    def get_indices(self):
        return self._indices
    indices = property(get_indices)


    # ---------------------------------
    def get_attributes(self):
        return self._attributes
    attributes = property(get_attributes)


    # ---------------------------------
    def clear(self):
        self._vertices.clear()
        self._indices.clear()
        self._dirty = True


    # ---------------------------------
    def append(self, vertices, indices, splits=None):
        vertices = np.array(vertices).astype(self._vertices.dtype).ravel()
        indices = np.array(indices).astype(self._indices.dtype).ravel()

        if splits is None:
            indices +=  len(self._vertices.data)
            self._vertices.append(vertices)
            self._indices.append(indices) 
            return
        
        splits = np.array(splits)
        if splits.size == 2:
            vsize,isize = splits[0], splits[1]
            if (vertices.size % vsize) != 0:
                raise( RuntimeError,
                       "Cannot split vertices data into %d pieces" % vsize)
            if (indices.size % isize) != 0:
                raise( RuntimeError,
                       "Cannot split indices data into %d pieces" % vsize)
            n = vertices.size // vsize
            I = indices.reshape(indices.size / isize, isize)
            I += (np.arange(n) * vsize).reshape(n,1)
            self._vertices.append(vertices, int(vsize))
            self._indices.append(indices, int(isize))
        else:
            vsize,isize = splits[:,0], splits[:,1]
            if (vertices.size % vsize.sum()) != 0:
                raise( RuntimeError,
                       "Cannot split vertices data into %d pieces" % vsize)
            if (indices.size % isize.sum()) != 0:
                raise( RuntimeError,
                       "Cannot split indices data into %d pieces" % vsize)
            I = np.repeat(vsize.cumsum(),isize)
            indices[isize[0]:] += I[:-isize[0]]
            self._vertices.append(vertices,vsize)
            self._indices.append(indices,isize) 
        self._dirty = True


    # ---------------------------------
    def __delitem__(self, key):
        vsize = len(self._vertices[key])
        _,_,dstart,_ = self._indices._get_indices(key)
        del self._vertices[key]
        del self._indices[key]
        self._indices.data[dstart:] -= vsize
        self._dirty = True


    # ---------------------------------
    def __getitem__(self, key):
        return self._vertices[key], self._indices[key]


    # ---------------------------------
    def __len__(self):
        return len(self.vertices)

    # ---------------------------------
    def attribute(self, name):
        for a in self._attributes:
            if a.name == name:
                return a

    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return

        if not self._vertices_id:
            self._vertices_id = gl.glGenBuffers(1)
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, self._vertices_id )
        gl.glBufferData( gl.GL_ARRAY_BUFFER, self._vertices.data, gl.GL_DYNAMIC_DRAW )
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 )

        if not self._indices_id:
            self._indices_id = gl.glGenBuffers(1)
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id )
        gl.glBufferData( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices.data, gl.GL_DYNAMIC_DRAW )
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, 0 )

        self._dirty = False


    # ---------------------------------
    def draw( self, mode=gl.GL_TRIANGLES ):

        if self._dirty:
            self.upload()
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, self._vertices_id )
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id )
        for attribute in self._attributes:
            attribute.enable()
        gl.glDrawElements( mode, len(self._indices.data), gl.GL_UNSIGNED_INT, None)
        gl.glBindBuffer( gl.GL_ELEMENT_ARRAY_BUFFER, 0 )
        gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 )
Exemple #10
0
class Collection(object):

    # ---------------------------------
    def __init__(self, vtype, utype):
        # Convert types to lists (in case they were already dtypes) such that
        # we can append new fields
        vtype = eval(str(np.dtype(vtype)))
        utype = eval(str(np.dtype(utype)))

        # We add a uniform index to access uniform data from texture
        vtype.append( ('a_index', 'f4') )

        # Check if given utype is made of float32 only
        rutype = dtype_reduce(utype)
        if type(rutype[0]) is not str or rutype[2] != 'float32':
            raise CollectionError("Uniform type cannot de reduced to float32 only")
        else:
            count = rutype[1]
            count2 = int(math.pow(2, math.ceil(math.log(count, 2))))
            if (count2 - count) > 0:
                utype.append(('unused', 'f4', count2-count))
            self._count = count2
            self.utype = utype
        
        self._vbuffer = VertexBuffer(vtype)
        self._ubuffer = DynamicBuffer( utype )
        self._ubuffer_id = 0
        self._max_texture_size = gl.glGetInteger(gl.GL_MAX_TEXTURE_SIZE)
        self._compute_ushape(1)
        self._ubuffer.reserve( self._ushape[1] / (count/4) )
        self._dirty = True


    # ---------------------------------
    def _compute_ushape(self, size=1):
        max_texsize = self._max_texture_size
        cols = max_texsize//(self._count/4)
        rows = (size // cols)+1
        self._ushape = rows, cols*(self._count/4), self._count


    # ---------------------------------
    def __len__(self):
        return len(self._vbuffer)


    # ---------------------------------
    def __getitem__(self, key):
        V = self._vbuffer.vertices[key]
        I = self._vbuffer.indices[key]
        U = self._ubuffer[key]
        return Item(self, key, V, I, U)


    # ---------------------------------
    def __delitem__(self, key):
        start,end = self._vbuffer.vertices.range(key)
        del self._vbuffer[key]
        del self._ubuffer[key]
        self._vbuffer.vertices.data['a_index'][start:] -= 1
        self._vbuffer._dirty = True
        self._dirty = True



    # ---------------------------------
    def get_vbuffer(self):
        return self._vbuffer
    vbuffer = property(get_vbuffer)

    # ---------------------------------
    def get_vertices(self):
        return self._vbuffer.vertices
    vertices = property(get_vertices)


    # ---------------------------------
    def get_indices(self):
        return self._vbuffer.indices
    indices = property(get_indices)


    # ---------------------------------
    def get_uniforms(self):
        return self._ubuffer
    uniforms = property(get_uniforms)


    # ---------------------------------
    def get_attributes(self):
        return self._vbuffer._attributes
    attributes = property(get_attributes)


    # ---------------------------------
    def __getattr__(self, name):
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self,'_ubuffer')
            if name in buffer.dtype.names:
                return buffer.data[name]
        return object.__getattribute__(self,name)


    # ---------------------------------
    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self,'_ubuffer')
            if name in buffer.dtype.names:
                buffer.data[name] = value
                # buffer._dirty = True
                object.__setattr__(self, '_dirty', True)
        object.__setattr__(self, name, value)


    # ---------------------------------
    def clear(self):
        self._vbuffer.clear()
        self._ubuffer.clear()
        self._dirty = True


    # ---------------------------------
    def append(self, vertices, indices, uniforms, splits=None):
        vertices = np.array(vertices).astype(self._vbuffer.vertices.dtype)
        indices  = np.array(indices).astype(self._vbuffer.indices.dtype)
        uniforms = np.array(uniforms).astype(self._ubuffer.dtype)

        if splits is None:
            vertices['a_index'] = len(self)
            self._vbuffer.append( vertices, indices )
            self._ubuffer.append( uniforms )
            self._compute_ushape(len(self))
        elif len(splits) == 2:
            vsize,isize = splits[0], splits[1]
            if (vertices.size % vsize) != 0:
                raise( RuntimeError,
                       "Cannot split vertices data into %d pieces" % vsize)
            if (indices.size % isize) != 0:
                raise( RuntimeError,
                       "Cannot split indices data into %d pieces" % vsize)
            vcount = vertices.size//vsize
            icount = indices.size//isize
            ucount = uniforms.size
            n = ucount
            if vcount != icount or vcount != ucount:
                raise( RuntimeError,
                       "Vertices/indices/uniforms cannot be split")
            vertices['a_index'] = len(self)+np.repeat(np.arange(n),vsize)
            self._vbuffer.append( vertices, indices, (vsize,isize))
            self._ubuffer.append( uniforms )
            self._compute_ushape(len(self))
        else:
            raise(RuntimeError, "Splits argument not understood")
        self._dirty = True


    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return
        self._vbuffer.upload()
        self.upload_uniforms()
        self._dirty = False


    # ---------------------------------
    def upload_uniforms(self):

        gl.glActiveTexture( gl.GL_TEXTURE0 )
        data = self._ubuffer.data.view(np.float32)
        shape = self._ushape

        if not self._ubuffer_id:
            self._ubuffer_id = gl.glGenTextures(1)

            gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
            gl.glPixelStorei( gl.GL_UNPACK_ALIGNMENT, 1 )
            gl.glPixelStorei( gl.GL_PACK_ALIGNMENT, 1 )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_BASE_LEVEL, 0)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0)
            gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                             shape[1], shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )
        gl.glActiveTexture( gl.GL_TEXTURE0 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
        gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                         shape[1], shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )


    # ---------------------------------
    def draw(self, mode=gl.GL_TRIANGLES, uniforms = {}):
        if self._dirty:
            self.upload()
        shader = self.shader
        shader.bind()
        gl.glActiveTexture( gl.GL_TEXTURE0 )
        shader.uniformi( 'u_uniforms', 0 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )
        for name,value in uniforms.items():
            shader.uniform(name, value)
        shader.uniformf('u_uniforms_shape', *self._ushape)
        self._vbuffer.draw(mode)
        shader.unbind()
class VertexBuffer(object):

    # ---------------------------------
    def __init__(self, dtype):
        # Parse vertices dtype and generate attributes
        gltypes = {
            'float32': gl.GL_FLOAT,
            'float': gl.GL_DOUBLE,
            'float64': gl.GL_DOUBLE,
            'int8': gl.GL_BYTE,
            'uint8': gl.GL_UNSIGNED_BYTE,
            'int16': gl.GL_SHORT,
            'uint16': gl.GL_UNSIGNED_SHORT,
            'int32': gl.GL_INT,
            'uint32': gl.GL_UNSIGNED_INT
        }
        dtype = np.dtype(dtype)
        names = dtype.names or []
        stride = dtype.itemsize
        offset = 0
        self._attributes = []
        for i, name in enumerate(names):
            if dtype[name].subdtype is not None:
                gtype = str(dtype[name].subdtype[0])
                count = reduce(lambda x, y: x * y, dtype[name].shape)
            else:
                gtype = str(dtype[name])
                count = 1
            if gtype not in gltypes.keys():
                raise VertexBufferException('Data type not understood')
            gltype = gltypes[gtype]
            attribute = VertexAttribute(name, count, gltype, stride, offset)
            self._attributes.append(attribute)
            offset += dtype[name].itemsize

        self._dsize = offset
        self._vertices = DynamicBuffer(dtype)
        self._indices = DynamicBuffer(np.uint32)
        self._vertices_id = 0
        self._indices_id = 0
        self._dirty = True

    # ---------------------------------
    def get_vertices(self):
        return self._vertices

    vertices = property(get_vertices)

    # ---------------------------------
    def get_indices(self):
        return self._indices

    indices = property(get_indices)

    # ---------------------------------
    def get_attributes(self):
        return self._attributes

    attributes = property(get_attributes)

    # ---------------------------------
    def clear(self):
        self._vertices.clear()
        self._indices.clear()
        self._dirty = True

    # ---------------------------------
    def append(self, vertices, indices, splits=None):
        vertices = np.array(vertices).astype(self._vertices.dtype).ravel()
        indices = np.array(indices).astype(self._indices.dtype).ravel()

        if splits is None:
            indices += len(self._vertices.data)
            self._vertices.append(vertices)
            self._indices.append(indices)
            return

        splits = np.array(splits)
        if splits.size == 2:
            vsize, isize = splits[0], splits[1]
            if (vertices.size % vsize) != 0:
                raise (RuntimeError,
                       "Cannot split vertices data into %d pieces" % vsize)
            if (indices.size % isize) != 0:
                raise (RuntimeError,
                       "Cannot split indices data into %d pieces" % vsize)
            n = vertices.size // vsize
            I = indices.reshape(indices.size / isize, isize)
            I += (np.arange(n) * vsize).reshape(n, 1)
            self._vertices.append(vertices, int(vsize))
            self._indices.append(indices, int(isize))
        else:
            vsize, isize = splits[:, 0], splits[:, 1]
            if (vertices.size % vsize.sum()) != 0:
                raise (RuntimeError,
                       "Cannot split vertices data into %d pieces" % vsize)
            if (indices.size % isize.sum()) != 0:
                raise (RuntimeError,
                       "Cannot split indices data into %d pieces" % vsize)
            I = np.repeat(vsize.cumsum(), isize)
            indices[isize[0]:] += I[:-isize[0]]
            self._vertices.append(vertices, vsize)
            self._indices.append(indices, isize)
        self._dirty = True

    # ---------------------------------
    def __delitem__(self, key):
        vsize = len(self._vertices[key])
        _, _, dstart, _ = self._indices._get_indices(key)
        del self._vertices[key]
        del self._indices[key]
        self._indices.data[dstart:] -= vsize
        self._dirty = True

    # ---------------------------------
    def __getitem__(self, key):
        return self._vertices[key], self._indices[key]

    # ---------------------------------
    def __len__(self):
        return len(self.vertices)

    # ---------------------------------
    def attribute(self, name):
        for a in self._attributes:
            if a.name == name:
                return a

    # ---------------------------------
    def upload(self):

        if not self._dirty:
            return

        if not self._vertices_id:
            self._vertices_id = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._vertices_id)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, self._vertices.data,
                        gl.GL_DYNAMIC_DRAW)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)

        if not self._indices_id:
            self._indices_id = gl.glGenBuffers(1)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id)
        gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self._indices.data,
                        gl.GL_DYNAMIC_DRAW)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0)

        self._dirty = False

    # ---------------------------------
    def draw(self, mode=gl.GL_TRIANGLES):

        if self._dirty:
            self.upload()
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._vertices_id)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._indices_id)
        for attribute in self._attributes:
            attribute.enable()
        gl.glDrawElements(mode, len(self._indices.data), gl.GL_UNSIGNED_INT,
                          None)
        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0)
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
Exemple #12
0
class GridCollection(Collection):

    # ---------------------------------
    def __init__(self, dash_atlas = None):
        self.vtype = np.dtype( [('a_texcoord', 'f4', 2)] )
        self.utype = np.dtype( [('translate',        'f4', 2),
                                ('scale',            'f4', 1),
                                ('rotate',           'f4', 1),
                                ('major_grid',       'f4', 2),
                                ('minor_grid',       'f4', 2),
                                ('major_tick_size',  'f4', 2),
                                ('minor_tick_size',  'f4', 2),
                                ('major_grid_color', 'f4', 4),
                                ('minor_grid_color', 'f4', 4),
                                ('major_tick_color', 'f4', 4),
                                ('minor_tick_color', 'f4', 4),
                                ('major_grid_width', 'f4', 1),
                                ('minor_grid_width', 'f4', 1),
                                ('major_tick_width', 'f4', 1),
                                ('minor_tick_width', 'f4', 1),
                                ('size',             'f4', 2),
                                ('offset',           'f4', 2),
                                ('zoom',             'f4', 1),
                                ('antialias',        'f4', 1),
                                ('major_dash_phase', 'f4', 1),
                                ('minor_dash_phase', 'f4', 1),
                                ('major_dash_index', 'f4', 1),
                                ('major_dash_period','f4', 1),
                                ('major_dash_caps',  'f4', 2),
                                ('minor_dash_period','f4', 1),
                                ('minor_dash_index', 'f4', 1),
                                ('minor_dash_caps',  'f4', 2)
                                ] )
        self.gtype = np.dtype( [('name', 'f4', (1024,4))] )
        Collection.__init__(self, self.vtype, self.utype)
        if dash_atlas is None:
            self.dash_atlas = DashAtlas()
        else:
            self.dash_atlas = dash_atlas
        shaders = os.path.join(os.path.dirname(__file__),'shaders')
        vertex_shader= os.path.join( shaders, 'grid.vert')
        fragment_shader= os.path.join( shaders, 'grid.frag')
        self.shader = Shader( open(vertex_shader).read(),
                              open(fragment_shader).read() )
        self._gbuffer = DynamicBuffer( self.gtype )
        self._gbuffer_shape = [0,4*1024]
        self._gbuffer_id = 0


    # ---------------------------------
    def append( self, size = (1,1), antialias = 1.0,
                translate = (0,0), scale = 1.0, rotate = 0.0,
                offset = (0,0), zoom = 1,
                major_grid = [64.,64.], minor_grid = [8.,8.],
                major_grid_color = np.array([0.0, 0.0, 0.0, 0.75]),
                minor_grid_color = np.array([0.0, 0.0, 0.0, 0.25]),
                major_tick_color = np.array([0.0, 0.0, 0.0, 1.0]),
                minor_tick_color = np.array([0.0, 0.0, 0.0, 1.0]),
                major_grid_width = 1.0,
                minor_grid_width = 1.0,
                major_tick_size = np.array([10.0,10.0]),
                minor_tick_size = np.array([ 5.0, 5.0]),
                major_tick_width = 1.5,
                minor_tick_width = 1.00,
                major_dash_pattern='dotted',
                major_dash_phase = 0.0,
                major_dash_caps = ('round','round'),
                minor_dash_pattern='dotted',
                minor_dash_phase = 0.0,
                minor_dash_caps = ('round','round') ):

        V, I, _ = self.bake()
        U = np.zeros(1, self.utype)
        U['translate']        = translate
        U['scale']            = scale
        U['rotate']           = rotate 
        U['major_grid']       = major_grid
        U['minor_grid']       = minor_grid
        U['major_tick_size']  = major_tick_size
        U['minor_tick_size']  = minor_tick_size
        U['major_grid_color'] = major_grid_color
        U['minor_grid_color'] = minor_grid_color
        U['major_tick_color'] = major_tick_color
        U['minor_tick_color'] = minor_tick_color
        U['major_grid_width'] = major_grid_width
        U['minor_grid_width'] = minor_grid_width
        U['major_tick_width'] = major_tick_width
        U['minor_tick_width'] = minor_tick_width
        U['size']             = size
        U['offset']           = offset
        U['zoom']             = zoom
        U['antialias']        = antialias 

        if self.dash_atlas:
            dash_index, dash_period = self.dash_atlas[major_dash_pattern]
            U['major_dash_phase']  = major_dash_phase
            U['major_dash_index']  = dash_index
            U['major_dash_period'] = dash_period
            U['major_dash_caps']   = ( self.caps.get(major_dash_caps[0], 'round'),
                                       self.caps.get(major_dash_caps[1], 'round') )

            dash_index, dash_period = self.dash_atlas[minor_dash_pattern]
            U['minor_dash_phase']  = minor_dash_phase
            U['minor_dash_index']  = dash_index
            U['minor_dash_period'] = dash_period
            U['minor_dash_caps']   = ( self.caps.get(minor_dash_caps[0], 'round'),
                                       self.caps.get(minor_dash_caps[1], 'round') )


        Collection.append(self,V,I,U)
        G = np.empty((1024,4), dtype='f4')
        G = G.ravel().view(self.gtype)
        self._gbuffer.append(G)
        index = len(self._gbuffer)
        self._gbuffer_shape = [index,4*1024]
        self.update_gbuffer(index-1)

    # ---------------------------------
    def set_major_grid(self, key, major_grid):
        self._ubuffer.data[key]['major_grid'][...] = major_grid
        self.update_gbuffer(key)

    # ---------------------------------
    def set_minor_grid(self, key, minor_grid):
        self._ubuffer.data[key]['minor_grid'][...] = minor_grid
        self.update_gbuffer(key)

    # ---------------------------------
    def set_zoom(self, key, zoom):
        self._ubuffer.data[key]['zoom'] = zoom
        self.update_gbuffer(key)

    # ---------------------------------
    def set_offset(self, key, offset):
        self._ubuffer.data[key]['offset'][...] = offset
        self.update_gbuffer(key)

    # ---------------------------------
    def set_size(self, key, size):
        self._ubuffer.data[key]['size'][...] = size
        self.update_gbuffer(key)

    # ---------------------------------
    def update_gbuffer(self, key):

        def find_closest(A, target):
            # A must be sorted
            idx = A.searchsorted(target)
            idx = np.clip(idx, 1, len(A)-1)
            left = A[idx-1]
            right = A[idx]
            idx -= target - left < right - target
            return idx

        major_grid = self._ubuffer.data[key]['major_grid']
        minor_grid = self._ubuffer.data[key]['minor_grid']
        zoom       = self._ubuffer.data[key]['zoom']
        offset     = self._ubuffer.data[key]['offset']
        w,h        = self._ubuffer.data[key]['size']
        n = 1024

        t1 = major_grid[0]*zoom
        t2 = minor_grid[0]*zoom
        t3 = major_grid[1]*zoom
        t4 = minor_grid[1]*zoom

        I1 = np.arange(np.fmod(offset[0],t1), np.fmod(offset[0],t1)+w+t1,t1)
        I2 = np.arange(np.fmod(offset[0],t2), np.fmod(offset[0],t2)+w+t2,t2)
        I3 = np.arange(np.fmod(offset[1],t3), np.fmod(offset[1],t3)+h+t3,t3)
        I4 = np.arange(np.fmod(offset[1],t4), np.fmod(offset[1],t4)+h+t4,t4)

        # I1 = np.logspace(np.log10(1), np.log10(2*w), 5)*zoom        
        # I2 = np.logspace(np.log10(1), np.log10(2*w), 50)*zoom
        # I3 = np.logspace(np.log10(1), np.log10(2*h), 5)*zoom
        # I4 = np.logspace(np.log10(1), np.log10(2*h), 50)*zoom

        # We are here in screen space and we want integer coordinates
        np.floor(I1,out=I1)
        np.floor(I2,out=I2)
        np.floor(I3,out=I3)
        np.floor(I4,out=I4)
        L = np.linspace(0,w,n)
        G = np.empty((1024,4), dtype='f4')
        G[:,0] = I1[find_closest(I1,L)]
        G[:,2] = I2[find_closest(I2,L)]
        L = np.linspace(0,h,n)
        G[:,1] = I3[find_closest(I3,L)]
        G[:,3] = I4[find_closest(I4,L)]
        G = G.ravel().view(self.gtype)
        self._gbuffer[key] = G
        self._dirty = True


    # ---------------------------------
    def bake(self):
        V = np.zeros( 4, dtype = self.vtype )
        V['a_texcoord'] = (0,0),(0,1), (1,0),(1,1) 
        I = np.array([0,1,2,1,2,3],dtype=np.int32)
        return V, I, 0


    # ---------------------------------
    def upload(self):
        if not self._dirty:
            return

        Collection.upload( self )

        gl.glActiveTexture( gl.GL_TEXTURE2 )
        data = self._gbuffer.data.view(np.float32)
        shape = len(self._gbuffer), 4*1024
        if not self._gbuffer_id:
            self._gbuffer_id = gl.glGenTextures(1)

            gl.glBindTexture( gl.GL_TEXTURE_2D, self._gbuffer_id )
            gl.glPixelStorei( gl.GL_UNPACK_ALIGNMENT, 1 )
            gl.glPixelStorei( gl.GL_PACK_ALIGNMENT, 1 )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf( gl.GL_TEXTURE_2D,
                                gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE )
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_BASE_LEVEL, 0)
            gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0)
            gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                             shape[1]//4, shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )

        gl.glActiveTexture( gl.GL_TEXTURE2 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._gbuffer_id )
        gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA32F,
                         shape[1]//4, shape[0], 0, gl.GL_RGBA, gl.GL_FLOAT, data )
        self._dirty = False

    # ---------------------------------
    def draw(self):
        if self._dirty:
            self.upload()

        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glEnable(gl.GL_BLEND)
        _,_,width,height = gl.glGetIntegerv( gl.GL_VIEWPORT )
        P = orthographic( 0, width, 0, height, -1, +1 )
        V = np.eye(4).astype( np.float32 )
        M = np.eye(4).astype( np.float32 )

        shader = self.shader
        shader.bind()
        gl.glActiveTexture( gl.GL_TEXTURE0 )
        shader.uniformi( 'u_uniforms', 0 )
        shape = self._ubuffer_shape
        shader.uniformf( 'u_uniforms_shape', shape[1]//4, shape[0])
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._ubuffer_id )

        if self.dash_atlas:
            gl.glActiveTexture( gl.GL_TEXTURE1 )
            shader.uniformi('u_dash_atlas', 1)
            gl.glBindTexture( gl.GL_TEXTURE_2D, self.dash_atlas.texture_id )

        gl.glActiveTexture( gl.GL_TEXTURE2 )
        gl.glBindTexture( gl.GL_TEXTURE_2D, self._gbuffer_id )
        shader.uniformi( 'u_gbuffer', 2 )
        shape = self._gbuffer_shape
        shader.uniformf( 'u_gbuffer_shape', shape[1]//4, shape[0])

        shader.uniform_matrixf( 'u_M', M )
        shader.uniform_matrixf( 'u_V', V )
        shader.uniform_matrixf( 'u_P', P )
        self._vbuffer.draw( )
        shader.unbind()