示例#1
0
    def __init__(self, width: int, height: int):
        """Create a LightLayer

        The size of a layer should ideally be of the same size and the screen.

        :param Tuple[int, int] size: Width and height of light layer
        """
        super().__init__(width, height)

        self._lights: List[Light] = []
        self._prev_target = None
        self._rebuild = False
        self._stride = 28
        self._buffer = self.ctx.buffer(reserve=self._stride * 100)
        self._vao = self.ctx.geometry([
            gl.BufferDescription(
                self._buffer,
                '2f 1f 1f 3f',
                ['in_vert', 'in_radius', 'in_attenuation', 'in_color'],
                normalized=['in_color'],
            ),
        ])
        self._light_program = self.ctx.load_program(
            vertex_shader=":resources:shaders/lights/point_lights_vs.glsl",
            geometry_shader=":resources:shaders/lights/point_lights_geo.glsl",
            fragment_shader=":resources:shaders/lights/point_lights_fs.glsl",
        )
        self._combine_program = self.ctx.load_program(
            vertex_shader=":resources:shaders/lights/combine_vs.glsl",
            fragment_shader=":resources:shaders/lights/combine_fs.glsl",
        )
        # NOTE: Diffuse buffer created in parent
        self._light_buffer = self.ctx.framebuffer(
            color_attachments=self.ctx.texture((width, height), components=3))
示例#2
0
        def _calculate_size_buffer():
            self._sprite_size_data = array.array('f')
            for sprite in self.sprite_list:
                self._sprite_size_data.append(sprite.width)
                self._sprite_size_data.append(sprite.height)

            self._sprite_size_buf = self.ctx.buffer(
                data=self._sprite_size_data, usage=usage)
            variables = ['in_size']
            self._sprite_size_desc = gl.BufferDescription(
                self._sprite_size_buf, '2f', variables)
            self._sprite_size_changed = False
示例#3
0
        def _calculate_angle_buffer():
            self._sprite_angle_data = array.array('f')
            for sprite in self.sprite_list:
                self._sprite_angle_data.append(sprite.angle)

            self._sprite_angle_buf = self.ctx.buffer(
                data=self._sprite_angle_data, usage=usage)
            variables = ['in_angle']
            self._sprite_angle_desc = gl.BufferDescription(
                self._sprite_angle_buf,
                '1f',
                variables,
            )
            self._sprite_angle_changed = False
示例#4
0
        def _calculate_colors():
            self._sprite_color_data = array.array('B')

            for sprite in self.sprite_list:
                self._sprite_color_data.extend(sprite.color[:3])
                self._sprite_color_data.append(int(sprite.alpha))

            self._sprite_color_buf = self.ctx.buffer(
                data=self._sprite_color_data, usage=usage)
            variables = ['in_color']
            self._sprite_color_desc = gl.BufferDescription(
                self._sprite_color_buf,
                '4f1',
                variables,
                normalized=['in_color'])
            self._sprite_color_changed = False
示例#5
0
        def _calculate_pos_buffer():
            self._sprite_pos_data = array.array('f')
            # print("A")
            for sprite in self.sprite_list:
                self._sprite_pos_data.append(sprite.center_x)
                self._sprite_pos_data.append(sprite.center_y)

            self._sprite_pos_buf = self.ctx.buffer(data=self._sprite_pos_data,
                                                   usage=usage)
            variables = ['in_pos']
            self._sprite_pos_desc = gl.BufferDescription(
                self._sprite_pos_buf,
                '2f',
                variables,
            )
            self._sprite_pos_changed = False
示例#6
0
        def _calculate_sub_tex_coords():
            """
            Create a sprite sheet, and set up subtexture coordinates to point
            to images in that sheet.
            """
            new_array_of_texture_names = []
            new_array_of_images = []
            new_texture = False
            if self.array_of_images is None or self._force_new_atlas_generation:
                new_texture = True
                self._force_new_atlas_generation = False

            # print()
            # print("New texture start: ", new_texture)

            for sprite in self.sprite_list:

                # noinspection PyProtectedMember
                if sprite.texture is None:
                    raise Exception("Error: Attempt to draw a sprite without a texture set.")

                name_of_texture_to_check = sprite.texture.name

                # Do we already have this in our old texture atlas?
                if name_of_texture_to_check not in self.array_of_texture_names:
                    # No, so flag that we'll have to create a new one.
                    new_texture = True
                    # print("New because of ", name_of_texture_to_check)

                # Do we already have this created because of a prior loop?
                if name_of_texture_to_check not in new_array_of_texture_names:
                    # No, so make as a new image
                    new_array_of_texture_names.append(name_of_texture_to_check)
                    if sprite.texture is None:
                        raise ValueError(f"Sprite has no texture.")
                    if sprite.texture.image is None:
                        raise ValueError(f"Sprite texture {sprite.texture.name} has no image.")
                    image = sprite.texture.image

                    # Create a new image with a transparent border around it to help prevent artifacts
                    tmp = Image.new('RGBA', (image.width+2, image.height+2))
                    tmp.paste(image, (1, 1))
                    tmp.paste(tmp.crop((1          , 1           , image.width+1, 2             )), (1            , 0             ))
                    tmp.paste(tmp.crop((1          , image.height, image.width+1, image.height+1)), (1            , image.height+1))
                    tmp.paste(tmp.crop((1          , 0           ,             2, image.height+2)), (0            , 0             ))
                    tmp.paste(tmp.crop((image.width, 0           , image.width+1, image.height+2)), (image.width+1, 0             ))

                    # Put in our array of new images
                    new_array_of_images.append(tmp)

            # print("New texture end: ", new_texture)
            # print(new_array_of_texture_names)
            # print(self.array_of_texture_names)
            # print()

            if new_texture:
                # Add back in any old textures. Chances are we'll need them.
                for index, old_texture_name in enumerate(self.array_of_texture_names):
                    if old_texture_name not in new_array_of_texture_names and self.array_of_images is not None:
                        new_array_of_texture_names.append(old_texture_name)
                        image = self.array_of_images[index]
                        new_array_of_images.append(image)

                self.array_of_texture_names = new_array_of_texture_names

                self.array_of_images = new_array_of_images
                # print(f"New Texture Atlas with names {self.array_of_texture_names}")

            # Get their sizes
            widths, heights = zip(*(i.size for i in self.array_of_images))

            grid_item_width, grid_item_height = max(widths), max(heights)
            image_count = len(self.array_of_images)
            root = math.sqrt(image_count)
            grid_width = int(math.sqrt(image_count))
            # print(f"\nimage_count={image_count}, root={root}")
            if root == grid_width:
                # Perfect square
                grid_height = grid_width
                # print("\nA")
            else:
                grid_height = grid_width
                grid_width += 1
                if grid_width * grid_height < image_count:
                    grid_height += 1
                # print("\nB")

            # Figure out sprite sheet size
            margin = 0

            sprite_sheet_width = (grid_item_width + margin) * grid_width
            sprite_sheet_height = (grid_item_height + margin) * grid_height

            if new_texture:

                # TODO: This code isn't valid, but I think some releasing might be in order.
                # if self.texture is not None:
                #     .Texture.release(self.texture_id)

                # Make the composite image
                new_image2 = Image.new('RGBA', (sprite_sheet_width, sprite_sheet_height))

                x_offset = 0
                for index, image in enumerate(self.array_of_images):

                    x = (index % grid_width) * (grid_item_width + margin)
                    y = (index // grid_width) * (grid_item_height + margin)

                    # print(f"Pasting {new_array_of_texture_names[index]} at {x, y}")

                    new_image2.paste(image, (x, y))
                    x_offset += image.size[0]

                # Create a texture out the composite image
                texture_bytes2 = new_image2.tobytes()
                self._texture = self.ctx.texture(
                    (new_image2.width, new_image2.height),
                    components=4,
                    data=texture_bytes2,
                )

                if self.texture_id is None:
                    self.texture_id = SpriteList.next_texture_id

                # new_image2.save("sprites.png")

            # Create a list with the coordinates of all the unique textures
            self._tex_coords = []
            offset = 1

            for index, image in enumerate(self.array_of_images):
                column = index % grid_width
                row = index // grid_width

                # Texture coordinates are reversed in y axis
                row = grid_height - row - 1

                x = column * (grid_item_width + margin) + offset
                y = row * (grid_item_height + margin) + offset

                # Because, coordinates are reversed
                y += (grid_item_height - (image.height - margin))

                normalized_x = x / sprite_sheet_width
                normalized_y = y / sprite_sheet_height

                start_x = normalized_x
                start_y = normalized_y

                normalized_width = (image.width-2*offset) / sprite_sheet_width
                normalized_height = (image.height-2*offset) / sprite_sheet_height

                # print(f"Fetching {new_array_of_texture_names[index]} at {row}, {column} => {x}, {y} normalized to {start_x:.2}, {start_y:.2} size {normalized_width}, {normalized_height}")

                self._tex_coords.append([start_x, start_y, normalized_width, normalized_height])

            # Go through each sprite and pull from the coordinate list, the proper
            # coordinates for that sprite's image.
            self._sprite_sub_tex_data = array.array('f')
            for sprite in self.sprite_list:
                index = self.array_of_texture_names.index(sprite.texture.name)
                for coord in self._tex_coords[index]:
                    self._sprite_sub_tex_data.append(coord)

            self._sprite_sub_tex_buf = self.ctx.buffer(
                data=self._sprite_sub_tex_data,
                usage=usage
            )

            self._sprite_sub_tex_desc = gl.BufferDescription(
                self._sprite_sub_tex_buf,
                '4f',
                ['in_sub_tex_coords'],
            )
            self._sprite_sub_tex_changed = False
示例#7
0
    def __init__(self, width, height, title):
        super().__init__(width, height, title, resizable=True)
        self.set_vsync(True)
        self.size = self.width // 4, self.height // 4

        # Two buffers on the gpu with positions
        self.buffer1 = self.ctx.buffer(
            data=array('f', gen_initial_data(self, *self.size)))
        self.buffer2 = self.ctx.buffer(reserve=self.buffer1.size)
        # Buffer with color for each point
        self.colors = self.ctx.buffer(data=array('f', gen_colors(*self.size)))

        # Geometry for drawing the two buffer variants.
        # We pad away the desired position and add the color data.
        self.geometry1 = self.ctx.geometry([
            gl.BufferDescription(self.buffer1, '2f 2x4', ['in_pos']),
            gl.BufferDescription(self.colors, '3f', ['in_color']),
        ])
        self.geometry2 = self.ctx.geometry([
            gl.BufferDescription(self.buffer2, '2f 2x4', ['in_pos']),
            gl.BufferDescription(self.colors, '3f', ['in_color']),
        ])

        # Transform geometry for the two buffers. This is used to move the points with a transform shader
        self.transform1 = self.ctx.geometry([
            gl.BufferDescription(self.buffer1, '2f 2f', ['in_pos', 'in_dest'])
        ])
        self.transform2 = self.ctx.geometry([
            gl.BufferDescription(self.buffer2, '2f 2f', ['in_pos', 'in_dest'])
        ])

        # Let's make the coordinate system match the viewport
        projection = arcade.create_orthogonal_projection(
            0, self.width, 0, self.height, -100, 100).flatten()

        # Draw the points with the the supplied color
        self.points_program = self.ctx.program(
            vertex_shader="""
            #version 330

            uniform mat4 projection;
            in vec2 in_pos;
            in vec3 in_color;
            out vec3 color;

            void main() {
                gl_Position = projection * vec4(in_pos, 0.0, 1.0);
                color = in_color;
            }
            """,
            fragment_shader="""
            #version 330

            in vec3 color;
            out vec4 fragColor;

            void main() {
                fragColor = vec4(color, 1.0);
            }
            """,
        )
        # Write the projection matrix to the program uniform
        self.points_program['projection'] = projection

        # Program altering the point location.
        # We constantly try to move the point to its desired location.
        # In addition we check the distance to the mouse pointer and move it if within a certain range.
        self.transform_program = self.ctx.program(vertex_shader="""
            #version 330

            uniform float dt; // time delta (between frames)
            uniform vec2 mouse_pos;

            in vec2 in_pos;
            in vec2 in_dest;

            out vec2 out_pos;
            out vec2 out_dest;

            void main() {
                out_dest = in_dest;
                // Slowly move the point towards the desired location
                vec2 dir = in_dest - in_pos;
                vec2 pos = in_pos + dir * dt;
                // Move the point away from the mouse position
                float dist = length(pos - mouse_pos);
                if (dist < 90.0) {
                    pos += (pos - mouse_pos) * dt * 10;
                }
                out_pos = pos;
            }
            """, )
        self.frame_time = 0
        self.last_time = time.time()
        self.mouse_pos = -100, -100