def __init__(self, use_spatial_hash=True, spatial_hash_cell_size=128, is_static=False, use_lights=False): """ Initialize the sprite list """ # List of sprites in the sprite list self.sprite_list = [] self.sprite_idx = dict() # Used in drawing optimization via OpenGL if use_lights: fragment_shader = FRAGMENT_SHADER_LIGHT else: fragment_shader = FRAGMENT_SHADER self.program = shader.program( vertex_shader=VERTEX_SHADER, fragment_shader=fragment_shader ) self.sprite_data = None self.sprite_data_buf = None self.texture_id = None self.texture = None self.vao = None self.vbo_buf = None self.array_of_texture_names = [] self.array_of_images = [] self.light_list = None self.ambient_light = (255, 255, 255) # Used in collision detection optimization self.spatial_hash = SpatialHash(cell_size=spatial_hash_cell_size) self.use_spatial_hash = use_spatial_hash self.is_static = is_static
def draw(self): """ Draw this list of sprites. """ if self.program is None: # Used in drawing optimization via OpenGL self.program = shader.program(vertex_shader=VERTEX_SHADER, fragment_shader=FRAGMENT_SHADER) if len(self.sprite_list) == 0: return if self.vao is None: self._calculate_sprite_buffer() self._texture.use(0) gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) # 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) with self.vao: self.program['Texture'] = self.texture_id self.program['Projection'] = get_projection().flatten() if not self.is_static: self.sprite_data_buf.write(self.sprite_data.tobytes()) self.vao.render(gl.GL_TRIANGLE_STRIP, instances=len(self.sprite_list)) if not self.is_static: self.sprite_data_buf.orphan()
def create_line(start_x: float, start_y: float, end_x: float, end_y: float, color: Color, line_width: float = 1) -> Shape: """ Create a line to be rendered later. This works faster than draw_line because the vertexes are only loaded to the graphics card once, rather than each frame. """ program = shader.program( vertex_shader=''' #version 330 uniform mat4 Projection; in vec2 in_vert; in vec4 in_color; out vec4 v_color; void main() { gl_Position = Projection * vec4(in_vert, 0.0, 1.0); v_color = in_color; } ''', fragment_shader=''' #version 330 in vec4 v_color; out vec4 f_color; void main() { f_color = v_color; } ''', ) buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')]) data = np.zeros(2, dtype=buffer_type) data['vertex'] = (start_x, start_y), (end_x, end_y) data['color'] = get_four_byte_color(color) vbo = shader.buffer(data.tobytes()) vao_content = [ shader.BufferDescription(vbo, '2f 4B', ('in_vert', 'in_color'), normalized=['in_color']) ] vao = shader.vertex_array(program, vao_content) with vao: program['Projection'] = get_projection().flatten() shape = Shape() shape.vao = vao shape.vbo = vbo shape.program = program shape.mode = gl.GL_LINE_STRIP shape.line_width = line_width return shape
def create_line_generic_with_colors(point_list: PointList, color_list: Iterable[Color], shape_mode: int, line_width: float=1) -> Shape: """ This function is used by ``create_line_strip`` and ``create_line_loop``, just changing the OpenGL type for the line drawing. """ program = shader.program( vertex_shader=''' #version 330 uniform mat4 Projection; in vec2 in_vert; in vec4 in_color; out vec4 v_color; void main() { gl_Position = Projection * vec4(in_vert, 0.0, 1.0); v_color = in_color; } ''', fragment_shader=''' #version 330 in vec4 v_color; out vec4 f_color; void main() { f_color = v_color; } ''', ) buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')]) data = np.zeros(len(point_list), dtype=buffer_type) data['vertex'] = point_list data['color'] = [get_four_byte_color(color) for color in color_list] vbo = shader.buffer(data.tobytes()) vao_content = [ shader.BufferDescription( vbo, '2f 4B', ('in_vert', 'in_color'), normalized=['in_color'] ) ] vao = shader.vertex_array(program, vao_content) with vao: program['Projection'] = get_projection().flatten() shape = Shape() shape.vao = vao shape.vbo = vbo shape.program = program shape.mode = shape_mode shape.line_width = line_width return shape
def _generic_draw_line_strip(point_list: PointList, color: Color, line_width: float=1, mode: int=moderngl.LINE_STRIP): """ Draw a line strip. A line strip is a set of continuously connected line segments. Args: :point_list: List of points making up the line. Each point is in a list. So it is a list of lists. :color: color, specified in a list of 3 or 4 bytes in RGB or RGBA format. :border_width: Width of the line in pixels. Returns: None Raises: None """ program = shader.program( vertex_shader=line_vertex_shader, fragment_shader=line_fragment_shader, ) buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')]) data = np.zeros(len(point_list), dtype=buffer_type) data['vertex'] = point_list color = get_four_byte_color(color) data['color'] = color vbo = shader.buffer(data.tobytes()) vbo_desc = shader.BufferDescription( vbo, '2f 4B', ('in_vert', 'in_color'), normalized=['in_color'] ) vao_content = [vbo_desc] vao = shader.vertex_array(program, vao_content) with vao: program['Projection'] = get_projection().flatten() gl.glLineWidth(line_width) gl.glPointSize(line_width) gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) gl.glEnable(gl.GL_LINE_SMOOTH) gl.glHint(gl.GL_LINE_SMOOTH_HINT, gl.GL_NICEST) gl.glHint(gl.GL_POLYGON_SMOOTH_HINT, gl.GL_NICEST) vao.render(mode=mode)
def _generic_draw_line_strip(point_list: PointList, color: Color, mode: int = gl.GL_LINE_STRIP): """ Draw a line strip. A line strip is a set of continuously connected line segments. :param point_list: List of points making up the line. Each point is in a list. So it is a list of lists. :param Color color: color, specified in a list of 3 or 4 bytes in RGB or RGBA format. """ # Cache the program. But not on linux because it fails unit tests for some reason. # if not _generic_draw_line_strip.program or sys.platform == "linux": _generic_draw_line_strip.program = shader.program( vertex_shader=_line_vertex_shader, fragment_shader=_line_fragment_shader, ) c4 = get_four_byte_color(color) c4e = c4 * len(point_list) a = array.array('B', c4e) color_buf = shader.buffer(a.tobytes()) color_buf_desc = shader.BufferDescription( color_buf, '4B', ['in_color'], normalized=['in_color'], ) def gen_flatten(my_list): return [item for sublist in my_list for item in sublist] vertices = array.array('f', gen_flatten(point_list)) vbo_buf = shader.buffer(vertices.tobytes()) vbo_buf_desc = shader.BufferDescription(vbo_buf, '2f', ['in_vert']) vao_content = [vbo_buf_desc, color_buf_desc] vao = shader.vertex_array(_generic_draw_line_strip.program, vao_content) with vao: _generic_draw_line_strip.program['Projection'] = get_projection( ).flatten() vao.render(mode=mode)
def __init__(self): """ Initialize the sprite list """ # List of sprites in the sprite list self.shape_list = [] self.change_x = 0 self.change_y = 0 self._center_x = 0 self._center_y = 0 self._angle = 0 self.program = shader.program( vertex_shader=''' #version 330 uniform mat4 Projection; uniform vec2 Position; uniform float Angle; in vec2 in_vert; in vec4 in_color; out vec4 v_color; void main() { float angle = radians(Angle); mat2 rotate = mat2( cos(angle), sin(angle), -sin(angle), cos(angle) ); gl_Position = Projection * vec4(Position + (rotate * in_vert), 0.0, 1.0); v_color = in_color; } ''', fragment_shader=''' #version 330 in vec4 v_color; out vec4 f_color; void main() { f_color = v_color; } ''', ) # Could do much better using just one vbo and glDrawElementsBaseVertex self.batches = defaultdict(_Batch) self.dirties = set()
def _generic_draw_line_strip(point_list: PointList, color: Color, mode: int = gl.GL_LINE_STRIP): """ Draw a line strip. A line strip is a set of continuously connected line segments. :param point_list: List of points making up the line. Each point is in a list. So it is a list of lists. :param Color color: color, specified in a list of 3 or 4 bytes in RGB or RGBA format. """ program = shader.program( vertex_shader=line_vertex_shader, fragment_shader=line_fragment_shader, ) buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')]) data = np.zeros(len(point_list), dtype=buffer_type) data['vertex'] = point_list color = get_four_byte_color(color) data['color'] = color vbo = shader.buffer(data.tobytes()) vbo_desc = shader.BufferDescription( vbo, '2f 4B', ('in_vert', 'in_color'), normalized=['in_color'] ) vao_content = [vbo_desc] vao = shader.vertex_array(program, vao_content) with vao: program['Projection'] = get_projection().flatten() vao.render(mode=mode)
def __init__(self): self.vao = None self._queue = deque() self._layers = deque() self.sprite_data_buf = None self.texture_id = None self._texture = None self.program = shader.program(vertex_shader=VERTEX_SHADER, fragment_shader=FRAGMENT_SHADER) self.array_of_texture_names = [] self.array_of_images = [] self.array_of_layers = [] self.is_static = False self.vertices = np.array( [ # x, y, u, v in GL Triangle Strip Mode -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, ], dtype=np.float32) self.ended = False self.times = []
def draw(self, **kwargs): """ Draw this list of sprites. :param filter: Optional parameter to set OpenGL filter, such as `gl.GL_NEAREST` to avoid smoothing. """ if self.program is None: # Used in drawing optimization via OpenGL self.program = shader.program(vertex_shader=_VERTEX_SHADER, fragment_shader=_FRAGMENT_SHADER) if len(self.sprite_list) == 0: return if self._vao1 is None: self._calculate_sprite_buffer() self._texture.use(0) gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) if "filter" in kwargs: gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, kwargs["filter"]) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, kwargs["filter"]) # 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) with self._vao1: self.program['Texture'] = self.texture_id self.program['Projection'] = get_projection().flatten() texture_transform = None if len(self.sprite_list) > 0: # always wrap texture transformations with translations # so that rotate and resize operations act on the texture # center by default texture_transform = Matrix3x3().translate(-0.5, -0.5).multiply( self.sprite_list[0].texture_transform.v).multiply( Matrix3x3().translate(0.5, 0.5).v) if texture_transform == None: texture_transform = Matrix3x3() self.program['TextureTransform'] = texture_transform.v if not self.is_static: if self._sprite_pos_changed: self._sprite_pos_buf.orphan() self._sprite_pos_buf.write(self._sprite_pos_data.tobytes()) self._sprite_pos_changed = False if self._sprite_size_changed: self._sprite_size_buf.orphan() self._sprite_size_buf.write( self._sprite_size_data.tobytes()) self._sprite_size_changed = False if self._sprite_angle_changed: self._sprite_angle_buf.orphan() self._sprite_angle_buf.write( self._sprite_angle_data.tobytes()) self._sprite_angle_changed = False if self._sprite_color_changed: self._sprite_color_buf.orphan() self._sprite_color_buf.write( self._sprite_color_data.tobytes()) self._sprite_color_changed = False if self._sprite_sub_tex_changed: self._sprite_sub_tex_buf.orphan() self._sprite_sub_tex_buf.write( self._sprite_sub_tex_data.tobytes()) self._sprite_sub_tex_changed = False self._vao1.render(gl.GL_TRIANGLE_STRIP, instances=len(self.sprite_list))
def draw(self, **kwargs): """ Draw this list of sprites. :param filter: Optional parameter to set OpenGL filter, such as `gl.GL_NEAREST` to avoid smoothing. """ if self.program is None: # Used in drawing optimization via OpenGL self.program = shader.program(vertex_shader=_VERTEX_SHADER, fragment_shader=_FRAGMENT_SHADER) if len(self.sprite_list) == 0: return if self._vao1 is None: self._calculate_sprite_buffer() self._texture.use(0) gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) if "filter" in kwargs: gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, kwargs["filter"]) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, kwargs["filter"]) # 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) with self._vao1: self.program['Texture'] = self.texture_id self.program['Projection'] = get_projection().flatten() if not self.is_static: if self._sprite_pos_changed: self._sprite_pos_buf.orphan() self._sprite_pos_buf.write(self._sprite_pos_data.tobytes()) self._sprite_pos_changed = False if self._sprite_size_changed: self._sprite_size_buf.orphan() self._sprite_size_buf.write( self._sprite_size_data.tobytes()) self._sprite_size_changed = False if self._sprite_angle_changed: self._sprite_angle_buf.orphan() self._sprite_angle_buf.write( self._sprite_angle_data.tobytes()) self._sprite_angle_changed = False if self._sprite_color_changed: self._sprite_color_buf.orphan() self._sprite_color_buf.write( self._sprite_color_data.tobytes()) self._sprite_color_changed = False if self._sprite_sub_tex_changed: self._sprite_sub_tex_buf.orphan() self._sprite_sub_tex_buf.write( self._sprite_sub_tex_data.tobytes()) self._sprite_sub_tex_changed = False self._vao1.render(gl.GL_TRIANGLE_STRIP, instances=len(self.sprite_list))
def setup_shader_program_if_needed(self): if self.program is None: self.program = shader.program(vertex_shader=VERTEX_SHADER, fragment_shader=FRAGMENT_SHADER)
def get_program(): if GLShader._PROGRAM is None: GLShader._PROGRAM = shader.program(vertex_shader=VERTEX_SHADER, fragment_shader=FRAGMENT_SHADER) return GLShader._PROGRAM