Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_sprite/src/sprite_mesh.rs
9353 views
1
use bevy_asset::{Assets, Handle};
2
use bevy_camera::visibility::{Visibility, VisibilityClass};
3
use bevy_color::Color;
4
use bevy_ecs::{component::Component, reflect::ReflectComponent};
5
use bevy_image::{Image, TextureAtlas, TextureAtlasLayout};
6
use bevy_math::{Rect, UVec2, Vec2};
7
use bevy_reflect::{std_traits::ReflectDefault, PartialReflect, Reflect};
8
use bevy_transform::components::Transform;
9
10
use crate::{Anchor, SpriteImageMode};
11
12
/// This is a carbon copy of [`Sprite`](crate::sprite::Sprite) that uses the
13
/// Mesh backend instead of the Sprite backend.
14
///
15
/// The only API difference is the added [`alpha mode`](SpriteMesh::alpha_mode).
16
#[derive(Component, Debug, Default, Clone, Reflect, PartialEq)]
17
#[require(Transform, Visibility, VisibilityClass, Anchor)]
18
#[reflect(Component, Default, Debug, Clone)]
19
pub struct SpriteMesh {
20
/// The image used to render the sprite
21
pub image: Handle<Image>,
22
/// The (optional) texture atlas used to render the sprite
23
pub texture_atlas: Option<TextureAtlas>,
24
/// The sprite's color tint
25
pub color: Color,
26
/// Flip the sprite along the `X` axis
27
pub flip_x: bool,
28
/// Flip the sprite along the `Y` axis
29
pub flip_y: bool,
30
/// An optional custom size for the sprite that will be used when rendering, instead of the size
31
/// of the sprite's image
32
pub custom_size: Option<Vec2>,
33
/// An optional rectangle representing the region of the sprite's image to render, instead of rendering
34
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].
35
///
36
/// When used with a [`TextureAtlas`], the rect
37
/// is offset by the atlas's minimal (top-left) corner position.
38
pub rect: Option<Rect>,
39
/// How the sprite's image will be scaled.
40
pub image_mode: SpriteImageMode,
41
/// The sprite's alpha mode, defaulting to `Mask(0.5)`.
42
/// If you wish to render a sprite with translucent pixels,
43
/// set it to `Blend` instead (significantly worse for performance).
44
pub alpha_mode: SpriteAlphaMode,
45
}
46
47
impl core::hash::Hash for SpriteMesh {
48
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
49
self.image.hash(state);
50
self.texture_atlas.hash(state);
51
self.color.reflect_hash().hash(state);
52
self.custom_size.reflect_hash().hash(state);
53
self.flip_x.hash(state);
54
self.flip_y.hash(state);
55
}
56
}
57
58
impl Eq for SpriteMesh {}
59
60
// NOTE: The SpriteImageMode, SpriteScalingMode and Anchor are imported from the sprite module.
61
62
impl SpriteMesh {
63
/// Create a Sprite with a custom size
64
pub fn sized(custom_size: Vec2) -> Self {
65
SpriteMesh {
66
custom_size: Some(custom_size),
67
..Default::default()
68
}
69
}
70
71
/// Create a sprite from an image
72
pub fn from_image(image: Handle<Image>) -> Self {
73
Self {
74
image,
75
..Default::default()
76
}
77
}
78
79
/// Create a sprite from an image, with an associated texture atlas
80
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
81
Self {
82
image,
83
texture_atlas: Some(atlas),
84
..Default::default()
85
}
86
}
87
88
/// Create a sprite from a solid color
89
pub fn from_color(color: impl Into<Color>, size: Vec2) -> Self {
90
Self {
91
color: color.into(),
92
custom_size: Some(size),
93
..Default::default()
94
}
95
}
96
97
/// Computes the pixel point where `point_relative_to_sprite` is sampled
98
/// from in this sprite. `point_relative_to_sprite` must be in the sprite's
99
/// local frame. Returns an Ok if the point is inside the bounds of the
100
/// sprite (not just the image), and returns an Err otherwise.
101
pub fn compute_pixel_space_point(
102
&self,
103
point_relative_to_sprite: Vec2,
104
anchor: Anchor,
105
images: &Assets<Image>,
106
texture_atlases: &Assets<TextureAtlasLayout>,
107
) -> Result<Vec2, Vec2> {
108
let image_size = images
109
.get(&self.image)
110
.map(Image::size)
111
.unwrap_or(UVec2::ONE);
112
113
let atlas_rect = self
114
.texture_atlas
115
.as_ref()
116
.and_then(|s| s.texture_rect(texture_atlases))
117
.map(|r| r.as_rect());
118
let texture_rect = match (atlas_rect, self.rect) {
119
(None, None) => Rect::new(0.0, 0.0, image_size.x as f32, image_size.y as f32),
120
(None, Some(sprite_rect)) => sprite_rect,
121
(Some(atlas_rect), None) => atlas_rect,
122
(Some(atlas_rect), Some(mut sprite_rect)) => {
123
// Make the sprite rect relative to the atlas rect.
124
sprite_rect.min += atlas_rect.min;
125
sprite_rect.max += atlas_rect.min;
126
sprite_rect
127
}
128
};
129
130
let sprite_size = self.custom_size.unwrap_or_else(|| texture_rect.size());
131
let sprite_center = -anchor.as_vec() * sprite_size;
132
133
let mut point_relative_to_sprite_center = point_relative_to_sprite - sprite_center;
134
135
if self.flip_x {
136
point_relative_to_sprite_center.x *= -1.0;
137
}
138
// Texture coordinates start at the top left, whereas world coordinates start at the bottom
139
// left. So flip by default, and then don't flip if `flip_y` is set.
140
if !self.flip_y {
141
point_relative_to_sprite_center.y *= -1.0;
142
}
143
144
if sprite_size.x == 0.0 || sprite_size.y == 0.0 {
145
return Err(point_relative_to_sprite_center);
146
}
147
148
let sprite_to_texture_ratio = {
149
let texture_size = texture_rect.size();
150
Vec2::new(
151
texture_size.x / sprite_size.x,
152
texture_size.y / sprite_size.y,
153
)
154
};
155
156
let point_relative_to_texture =
157
point_relative_to_sprite_center * sprite_to_texture_ratio + texture_rect.center();
158
159
// TODO: Support `SpriteImageMode`.
160
161
if texture_rect.contains(point_relative_to_texture) {
162
Ok(point_relative_to_texture)
163
} else {
164
Err(point_relative_to_texture)
165
}
166
}
167
}
168
169
// This is different from AlphaMode2d in bevy_sprite_render because that crate depends on this one,
170
// so using it would've been caused a circular dependency. An option would be to move the Enum here
171
// but it uses a bevy_render dependency in its documentation, and I wanted to avoid bringing that
172
// dependency to this crate.
173
174
// NOTE: If this is ever replaced by AlphaMode2d, make a custom Default impl for Sprite,
175
// because AlphaMode2d defaults to Opaque, but the sprite's alpha mode is most commonly Mask(0.5)
176
177
#[derive(Debug, Reflect, Copy, Clone, PartialEq)]
178
#[reflect(Default, Debug, Clone)]
179
pub enum SpriteAlphaMode {
180
/// Base color alpha values are overridden to be fully opaque (1.0).
181
Opaque,
182
/// Reduce transparency to fully opaque or fully transparent
183
/// based on a threshold.
184
///
185
/// Compares the base color alpha value to the specified threshold.
186
/// If the value is below the threshold,
187
/// considers the color to be fully transparent (alpha is set to 0.0).
188
/// If it is equal to or above the threshold,
189
/// considers the color to be fully opaque (alpha is set to 1.0).
190
Mask(f32),
191
/// The base color alpha value defines the opacity of the color.
192
/// Standard alpha-blending is used to blend the fragment's color
193
/// with the color behind it.
194
Blend,
195
}
196
197
impl Default for SpriteAlphaMode {
198
fn default() -> Self {
199
Self::Mask(0.5)
200
}
201
}
202
203