def load_program( self, *, vertex_shader: Union[str, Path], fragment_shader: Union[str, Path] = None, geometry_shader: Union[str, Path] = None, defines: dict = None) -> Program: """Create a new program given a file names that contain the vertex shader and fragment shader. :param Union[str,pathlib.Path] vertex_shader: path to vertex shader :param Union[str,pathlib.Path] fragment_shader: path to fragment shader :param Union[str,pathlib.Path] geometry_shader: path to geometry shader :param dict defines: Substitute ``#define`` values in the source """ from arcade.resources import resolve_resource_path vertex_shader_src = resolve_resource_path(vertex_shader).read_text() fragment_shader_src = None geometry_shader_src = None if fragment_shader: fragment_shader_src = resolve_resource_path(fragment_shader).read_text() if geometry_shader: geometry_shader_src = resolve_resource_path(geometry_shader).read_text() return self.program( vertex_shader=vertex_shader_src, fragment_shader=fragment_shader_src, geometry_shader=geometry_shader_src, defines=defines, )
def load_program( self, *, vertex_shader: Union[str, Path], fragment_shader: Union[str, Path] = None, geometry_shader: Union[str, Path] = None, tess_control_shader: Union[str, Path] = None, tess_evaluation_shader: Union[str, Path] = None, defines: dict = None, ) -> Program: """Create a new program given a file names that contain the vertex shader and fragment shader. Note that fragment and geometry shader are optional for when transform shaders are loaded. This method also supports the ``:resources:`` prefix. It's recommended to use absolute paths, but not required. Example:: # The most common use case if having a vertex and fragment shader program = window.ctx.load_program( vertex_shader="vert.glsl", fragment_shader="frag.glsl", ) :param Union[str,pathlib.Path] vertex_shader: path to vertex shader :param Union[str,pathlib.Path] fragment_shader: path to fragment shader (optional) :param Union[str,pathlib.Path] geometry_shader: path to geometry shader (optional) :param dict defines: Substitute ``#define`` values in the source """ from arcade.resources import resolve_resource_path vertex_shader_src = resolve_resource_path(vertex_shader).read_text() fragment_shader_src = None geometry_shader_src = None tess_control_src = None tess_evaluation_src = None if fragment_shader: fragment_shader_src = resolve_resource_path(fragment_shader).read_text() if geometry_shader: geometry_shader_src = resolve_resource_path(geometry_shader).read_text() if tess_control_shader and tess_evaluation_shader: tess_control_src = resolve_resource_path(tess_control_shader).read_text() tess_evaluation_src = resolve_resource_path(tess_evaluation_shader).read_text() return self.program( vertex_shader=vertex_shader_src, fragment_shader=fragment_shader_src, geometry_shader=geometry_shader_src, tess_control_shader=tess_control_src, tess_evaluation_shader=tess_evaluation_src, defines=defines, )
def load_texture( self, path: Union[str, Path], *, flip: bool = True, build_mipmaps=False, ) -> Texture: """Loads and creates an OpenGL 2D texture. Currently all textures are converted to RGBA. Example:: texture = window.ctx.load_texture("background.png") :param Union[str,pathlib.Path] path: Path to texture :param bool flip: Flips the image upside down :param bool build_mipmaps: Build mipmaps for the texture """ from arcade.resources import resolve_resource_path path = resolve_resource_path(path) image = Image.open(str(path)) if flip: image = image.transpose(Image.FLIP_TOP_BOTTOM) texture = self.texture(image.size, components=4, data=image.convert("RGBA").tobytes()) image.close() if build_mipmaps: texture.build_mipmaps() return texture
def read_tmx(tmx_file: Union[str, Path]) -> pytiled_parser.objects.TileMap: """ Given a .tmx, this will read in a tiled map, and return a TiledMap object. Given a tsx_file, the map will use it as the tileset. If tsx_file is not specified, it will use the tileset specified within the tmx_file. Important: Tiles must be a "collection" of images. Hitboxes can be drawn around tiles in the tileset editor, but only polygons are supported. (This is a great area for PR's to improve things.) :param str tmx_file: String with name of our TMX file :returns: Map :rtype: TiledMap """ # If we should pull from local resources, replace with proper path tmx_file = resolve_resource_path(tmx_file) tile_map = pytiled_parser.parse_tile_map(tmx_file) return tile_map
def load_spritesheet(file_name: Union[str, Path], sprite_width: int, sprite_height: int, columns: int, count: int, margin: int = 0) -> List[Texture]: """ :param str file_name: Name of the file to that holds the texture. :param int sprite_width: Width of the sprites in pixels :param int sprite_height: Height of the sprites in pixels :param int columns: Number of tiles wide the image is. :param int count: Number of tiles in the image. :param int margin: Margin between images :returns List: List of :class:`Texture` objects. """ texture_list = [] # If we should pull from local resources, replace with proper path file_name = resolve_resource_path(file_name) source_image = PIL.Image.open(file_name).convert('RGBA') for sprite_no in range(count): row = sprite_no // columns column = sprite_no % columns start_x = (sprite_width + margin) * column start_y = (sprite_height + margin) * row image = source_image.crop((start_x, start_y, start_x + sprite_width, start_y + sprite_height)) texture = Texture(f"{file_name}-{sprite_no}", image) texture_list.append(texture) return texture_list
def default_style(cls): """ :return: empty style # TODO maybe load the real default style once """ if cls.__default_style is None: cls.__default_style = UIStyle.from_file( resolve_resource_path(':resources:style/default.yml')) return cls.__default_style
def __init__(self, file_name: Union[str, Path], streaming: bool = False): self.file_name: str = "" file_name = resolve_resource_path(file_name) if not Path(file_name).is_file(): raise FileNotFoundError( f"The sound file '{file_name}' is not a file or can't be read." ) self.file_name = str(file_name) self.source: Union[media.StaticSource, media.StreamingSource] = media.load(self.file_name, streaming=streaming) self.min_distance = 100000000 #setting the players to this allows for 2D panning with 3D audio
def __init__(self, width, height, title): super().__init__(width, height, title) self.time = 0 self.program = self.ctx.program(vertex_shader=""" #version 330 in vec2 in_vert; in vec2 in_uv; out vec2 v_uv; void main() { gl_Position = vec4(in_vert, 0.0, 1.0); v_uv = in_uv; } """, fragment_shader=""" #version 330 uniform sampler2D background; uniform float time; in vec2 v_uv; out vec4 out_color; void main() { vec2 pos = v_uv * 1.0 - vec2(0.5); vec2 uv = v_uv + normalize(pos) + sin(length(pos) * 10 - time); out_color = texture(background, uv); } """) self.quad = geometry.screen_rectangle(-1, -1, 2, 2) image = Image.open( resolve_resource_path( ":resources:images/backgrounds/abstract_1.jpg")).convert( "RGBA") image = image.transpose(Image.FLIP_TOP_BOTTOM) self.texture = self.ctx.texture(image.size, components=4, data=image.tobytes())
def __init__(self, file_name: Union[str, Path], streaming: bool = False): """ Create and load the sound. """ self.file_name: str = "" self.wav_file: Union[soloud.WavStream, soloud.Wav] self.voice_handle = None # needed for volume control if not _audiolib: return # If we should pull from local resources, replace with proper path file_name = resolve_resource_path(file_name) if not Path(file_name).is_file(): raise FileNotFoundError( f"The sound file '{file_name}' is not a file or can't be read") self.file_name = str(file_name) if streaming: self.wav_file = soloud.WavStream() else: self.wav_file = soloud.Wav() self.wav_file.load(self.file_name)
def load_texture(file_name: Union[str, Path], x: float = 0, y: float = 0, width: float = 0, height: float = 0, flipped_horizontally: bool = False, flipped_vertically: bool = False, flipped_diagonally: bool = False, can_cache: bool = True, mirrored=None, hit_box_algorithm="Simple", hit_box_detail: float = 4.5) -> Texture: """ Load an image from disk and create a texture. Note: If the code is to load only part of the image, the given `x`, `y` coordinates will start with the origin `(0, 0)` in the upper left of the image. When drawing, Arcade uses `(0, 0)` in the lower left corner. Be careful with this reversal. For a longer explanation of why computers sometimes start in the upper left, see: http://programarcadegames.com/index.php?chapter=introduction_to_graphics&lang=en#section_5 :param str file_name: Name of the file to that holds the texture. :param float x: X position of the crop area of the texture. :param float y: Y position of the crop area of the texture. :param float width: Width of the crop area of the texture. :param float height: Height of the crop area of the texture. :param bool flipped_horizontally: Mirror the sprite image. Flip left/right across vertical axis. :param bool flipped_vertically: Flip the image up/down across the horizontal axis. :param bool flipped_diagonally: Transpose the image, flip it across the diagonal. :param bool can_cache: If a texture has already been loaded, load_texture will return the same texture in order \ to save time. Sometimes this is not desirable, as resizing a cached texture will cause all other textures to \ resize with it. Setting can_cache to false will prevent this issue at the experience of additional resources. :param bool mirrored: Deprecated. :param str hit_box_algorithm: One of 'None', 'Simple' or 'Detailed'. \ Defaults to 'Simple'. Use 'Simple' for the :data:`PhysicsEngineSimple`, \ :data:`PhysicsEnginePlatformer` \ and 'Detailed' for the :data:`PymunkPhysicsEngine`. .. figure:: images/hit_box_algorithm_none.png :width: 40% hit_box_algorithm = "None" .. figure:: images/hit_box_algorithm_simple.png :width: 55% hit_box_algorithm = "Simple" .. figure:: images/hit_box_algorithm_detailed.png :width: 75% hit_box_algorithm = "Detailed" :param float hit_box_detail: Float, defaults to 4.5. Used with 'Detailed' to hit box :returns: New :class:`Texture` object. :raises: ValueError """ if mirrored is not None: from warnings import warn warn( "In load_texture, the 'mirrored' parameter is deprecated. Use 'flipped_horizontally' instead.", DeprecationWarning) flipped_horizontally = mirrored # See if we already loaded this texture, and we can just use a cached version. cache_name = "{}-{}-{}-{}-{}-{}-{}-{}-{}".format( file_name, x, y, width, height, flipped_horizontally, flipped_vertically, flipped_diagonally, hit_box_algorithm) if can_cache and cache_name in load_texture.texture_cache: # type: ignore # dynamic attribute on function obj return load_texture.texture_cache[ cache_name] # type: ignore # dynamic attribute on function obj # See if we already loaded this texture file, and we can just use a cached version. cache_file_name = f"{file_name}" if cache_file_name in load_texture.texture_cache: # type: ignore # dynamic attribute on function obj texture = load_texture.texture_cache[ cache_file_name] # type: ignore # dynamic attribute on function obj source_image = texture.image else: # If we should pull from local resources, replace with proper path file_name = resolve_resource_path(file_name) source_image = PIL.Image.open(file_name).convert('RGBA') result = Texture(cache_file_name, source_image, hit_box_algorithm=hit_box_algorithm, hit_box_detail=hit_box_detail) load_texture.texture_cache[ cache_file_name] = result # type: ignore # dynamic attribute on function obj source_image_width, source_image_height = source_image.size if x != 0 or y != 0 or width != 0 or height != 0: if x > source_image_width: raise ValueError("Can't load texture starting at an x of {} " "when the image is only {} across.".format( x, source_image_width)) if y > source_image_height: raise ValueError("Can't load texture starting at an y of {} " "when the image is only {} high.".format( y, source_image_height)) if x + width > source_image_width: raise ValueError("Can't load texture ending at an x of {} " "when the image is only {} wide.".format( x + width, source_image_width)) if y + height > source_image_height: raise ValueError("Can't load texture ending at an y of {} " "when the image is only {} high.".format( y + height, source_image_height)) image = source_image.crop((x, y, x + width, y + height)) else: image = source_image # image = _trim_image(image) if flipped_diagonally: image = image.transpose(PIL.Image.TRANSPOSE) if flipped_horizontally: image = image.transpose(PIL.Image.FLIP_LEFT_RIGHT) if flipped_vertically: image = image.transpose(PIL.Image.FLIP_TOP_BOTTOM) result = Texture(cache_name, image, hit_box_algorithm=hit_box_algorithm, hit_box_detail=hit_box_detail) load_texture.texture_cache[ cache_name] = result # type: ignore # dynamic attribute on function obj return result
def load_textures(file_name: Union[str, Path], image_location_list: RectList, mirrored: bool = False, flipped: bool = False) -> List[Texture]: """ Load a set of textures from a single image file. Note: If the code is to load only part of the image, the given `x`, `y` coordinates will start with the origin `(0, 0)` in the upper left of the image. When drawing, Arcade uses `(0, 0)` in the lower left corner. Be careful with this reversal. For a longer explanation of why computers sometimes start in the upper left, see: http://programarcadegames.com/index.php?chapter=introduction_to_graphics&lang=en#section_5 :param str file_name: Name of the file. :param List image_location_list: List of image sub-locations. Each rectangle should be a `List` of four floats: `[x, y, width, height]`. :param bool mirrored: If set to `True`, the image is mirrored left to right. :param bool flipped: If set to `True`, the image is flipped upside down. :returns: List of :class:`Texture`'s. :raises: ValueError """ # See if we already loaded this texture file, and we can just use a cached version. cache_file_name = "{}".format(file_name) if cache_file_name in load_texture.texture_cache: # type: ignore # dynamic attribute on function obj texture = load_texture.texture_cache[ cache_file_name] # type: ignore # dynamic attribute on function obj source_image = texture.image else: file_name = resolve_resource_path(file_name) source_image = PIL.Image.open(file_name) result = Texture(cache_file_name, source_image) load_texture.texture_cache[ cache_file_name] = result # type: ignore # dynamic attribute on function obj source_image_width, source_image_height = source_image.size texture_info_list = [] for image_location in image_location_list: x, y, width, height = image_location if width <= 0: raise ValueError( "Texture has a width of {}, must be > 0.".format(width)) if x > source_image_width: raise ValueError("Can't load texture starting at an x of {} " "when the image is only {} across.".format( x, source_image_width)) if y > source_image_height: raise ValueError("Can't load texture starting at an y of {} " "when the image is only {} high.".format( y, source_image_height)) if x + width > source_image_width: raise ValueError("Can't load texture ending at an x of {} " "when the image is only {} wide.".format( x + width, source_image_width)) if y + height > source_image_height: raise ValueError("Can't load texture ending at an y of {} " "when the image is only {} high.".format( y + height, source_image_height)) # See if we already loaded this texture, and we can just use a cached version. cache_name = "{}{}{}{}{}{}{}".format(file_name, x, y, width, height, flipped, mirrored) if cache_name in load_texture.texture_cache: # type: ignore # dynamic attribute on function obj result = load_texture.texture_cache[ cache_name] # type: ignore # dynamic attribute on function obj else: image = source_image.crop((x, y, x + width, y + height)) # image = _trim_image(image) if mirrored: image = PIL.ImageOps.mirror(image) if flipped: image = PIL.ImageOps.flip(image) result = Texture(cache_name, image) load_texture.texture_cache[ cache_name] = result # type: ignore # dynamic attribute on function obj texture_info_list.append(result) return texture_info_list