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 __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 __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 __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 __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
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 )
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 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 )
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)
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()