use bevy_asset::{Assets, Handle};1use bevy_camera::visibility::{Visibility, VisibilityClass};2use bevy_color::Color;3use bevy_ecs::{component::Component, reflect::ReflectComponent};4use bevy_image::{Image, TextureAtlas, TextureAtlasLayout};5use bevy_math::{Rect, UVec2, Vec2};6use bevy_reflect::{std_traits::ReflectDefault, PartialReflect, Reflect};7use bevy_transform::components::Transform;89use crate::{Anchor, SpriteImageMode};1011/// This is a carbon copy of [`Sprite`](crate::sprite::Sprite) that uses the12/// Mesh backend instead of the Sprite backend.13///14/// The only API difference is the added [`alpha mode`](SpriteMesh::alpha_mode).15#[derive(Component, Debug, Default, Clone, Reflect, PartialEq)]16#[require(Transform, Visibility, VisibilityClass, Anchor)]17#[reflect(Component, Default, Debug, Clone)]18pub struct SpriteMesh {19/// The image used to render the sprite20pub image: Handle<Image>,21/// The (optional) texture atlas used to render the sprite22pub texture_atlas: Option<TextureAtlas>,23/// The sprite's color tint24pub color: Color,25/// Flip the sprite along the `X` axis26pub flip_x: bool,27/// Flip the sprite along the `Y` axis28pub flip_y: bool,29/// An optional custom size for the sprite that will be used when rendering, instead of the size30/// of the sprite's image31pub custom_size: Option<Vec2>,32/// An optional rectangle representing the region of the sprite's image to render, instead of rendering33/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].34///35/// When used with a [`TextureAtlas`], the rect36/// is offset by the atlas's minimal (top-left) corner position.37pub rect: Option<Rect>,38/// How the sprite's image will be scaled.39pub image_mode: SpriteImageMode,40/// The sprite's alpha mode, defaulting to `Mask(0.5)`.41/// If you wish to render a sprite with translucent pixels,42/// set it to `Blend` instead (significantly worse for performance).43pub alpha_mode: SpriteAlphaMode,44}4546impl core::hash::Hash for SpriteMesh {47fn hash<H: core::hash::Hasher>(&self, state: &mut H) {48self.image.hash(state);49self.texture_atlas.hash(state);50self.color.reflect_hash().hash(state);51self.custom_size.reflect_hash().hash(state);52self.flip_x.hash(state);53self.flip_y.hash(state);54}55}5657impl Eq for SpriteMesh {}5859// NOTE: The SpriteImageMode, SpriteScalingMode and Anchor are imported from the sprite module.6061impl SpriteMesh {62/// Create a Sprite with a custom size63pub fn sized(custom_size: Vec2) -> Self {64SpriteMesh {65custom_size: Some(custom_size),66..Default::default()67}68}6970/// Create a sprite from an image71pub fn from_image(image: Handle<Image>) -> Self {72Self {73image,74..Default::default()75}76}7778/// Create a sprite from an image, with an associated texture atlas79pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {80Self {81image,82texture_atlas: Some(atlas),83..Default::default()84}85}8687/// Create a sprite from a solid color88pub fn from_color(color: impl Into<Color>, size: Vec2) -> Self {89Self {90color: color.into(),91custom_size: Some(size),92..Default::default()93}94}9596/// Computes the pixel point where `point_relative_to_sprite` is sampled97/// from in this sprite. `point_relative_to_sprite` must be in the sprite's98/// local frame. Returns an Ok if the point is inside the bounds of the99/// sprite (not just the image), and returns an Err otherwise.100pub fn compute_pixel_space_point(101&self,102point_relative_to_sprite: Vec2,103anchor: Anchor,104images: &Assets<Image>,105texture_atlases: &Assets<TextureAtlasLayout>,106) -> Result<Vec2, Vec2> {107let image_size = images108.get(&self.image)109.map(Image::size)110.unwrap_or(UVec2::ONE);111112let atlas_rect = self113.texture_atlas114.as_ref()115.and_then(|s| s.texture_rect(texture_atlases))116.map(|r| r.as_rect());117let texture_rect = match (atlas_rect, self.rect) {118(None, None) => Rect::new(0.0, 0.0, image_size.x as f32, image_size.y as f32),119(None, Some(sprite_rect)) => sprite_rect,120(Some(atlas_rect), None) => atlas_rect,121(Some(atlas_rect), Some(mut sprite_rect)) => {122// Make the sprite rect relative to the atlas rect.123sprite_rect.min += atlas_rect.min;124sprite_rect.max += atlas_rect.min;125sprite_rect126}127};128129let sprite_size = self.custom_size.unwrap_or_else(|| texture_rect.size());130let sprite_center = -anchor.as_vec() * sprite_size;131132let mut point_relative_to_sprite_center = point_relative_to_sprite - sprite_center;133134if self.flip_x {135point_relative_to_sprite_center.x *= -1.0;136}137// Texture coordinates start at the top left, whereas world coordinates start at the bottom138// left. So flip by default, and then don't flip if `flip_y` is set.139if !self.flip_y {140point_relative_to_sprite_center.y *= -1.0;141}142143if sprite_size.x == 0.0 || sprite_size.y == 0.0 {144return Err(point_relative_to_sprite_center);145}146147let sprite_to_texture_ratio = {148let texture_size = texture_rect.size();149Vec2::new(150texture_size.x / sprite_size.x,151texture_size.y / sprite_size.y,152)153};154155let point_relative_to_texture =156point_relative_to_sprite_center * sprite_to_texture_ratio + texture_rect.center();157158// TODO: Support `SpriteImageMode`.159160if texture_rect.contains(point_relative_to_texture) {161Ok(point_relative_to_texture)162} else {163Err(point_relative_to_texture)164}165}166}167168// This is different from AlphaMode2d in bevy_sprite_render because that crate depends on this one,169// so using it would've been caused a circular dependency. An option would be to move the Enum here170// but it uses a bevy_render dependency in its documentation, and I wanted to avoid bringing that171// dependency to this crate.172173// NOTE: If this is ever replaced by AlphaMode2d, make a custom Default impl for Sprite,174// because AlphaMode2d defaults to Opaque, but the sprite's alpha mode is most commonly Mask(0.5)175176#[derive(Debug, Reflect, Copy, Clone, PartialEq)]177#[reflect(Default, Debug, Clone)]178pub enum SpriteAlphaMode {179/// Base color alpha values are overridden to be fully opaque (1.0).180Opaque,181/// Reduce transparency to fully opaque or fully transparent182/// based on a threshold.183///184/// Compares the base color alpha value to the specified threshold.185/// If the value is below the threshold,186/// considers the color to be fully transparent (alpha is set to 0.0).187/// If it is equal to or above the threshold,188/// considers the color to be fully opaque (alpha is set to 1.0).189Mask(f32),190/// The base color alpha value defines the opacity of the color.191/// Standard alpha-blending is used to blend the fragment's color192/// with the color behind it.193Blend,194}195196impl Default for SpriteAlphaMode {197fn default() -> Self {198Self::Mask(0.5)199}200}201202203