bevy/crates/bevy_sprite/src/texture_atlas.rs

156 lines
4.9 KiB
Rust
Raw Normal View History

use crate::{Anchor, Rect};
2020-06-04 02:00:19 +00:00
use bevy_asset::Handle;
use bevy_ecs::component::Component;
2020-07-17 00:23:50 +00:00
use bevy_math::Vec2;
use bevy_reflect::{Reflect, TypeUuid};
use bevy_render::{color::Color, texture::Image};
use bevy_utils::HashMap;
2020-06-01 06:39:20 +00:00
/// An atlas containing multiple textures (like a spritesheet or a tilemap).
/// [Example usage animating sprite.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
/// [Example usage loading sprite sheet.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
#[derive(Debug, Clone, TypeUuid)]
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
2020-06-06 07:12:38 +00:00
pub struct TextureAtlas {
2020-08-25 00:57:10 +00:00
/// The handle to the texture in which the sprites are stored
pub texture: Handle<Image>,
2020-06-04 02:00:19 +00:00
// TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer
pub size: Vec2,
2020-08-25 00:57:10 +00:00
/// The specific areas of the atlas where each texture can be found
2020-06-06 07:12:38 +00:00
pub textures: Vec<Rect>,
pub texture_handles: Option<HashMap<Handle<Image>, usize>>,
}
#[derive(Component, Debug, Clone, Reflect)]
2020-06-06 07:12:38 +00:00
pub struct TextureAtlasSprite {
pub color: Color,
pub index: usize,
Add Sprite Flipping (#1407) OK, here's my attempt at sprite flipping. There are a couple of points that I need review/help on, but I think the UX is about ideal: ```rust .spawn(SpriteBundle { material: materials.add(texture_handle.into()), sprite: Sprite { // Flip the sprite along the x axis flip: SpriteFlip { x: true, y: false }, ..Default::default() }, ..Default::default() }); ``` Now for the issues. The big issue is that for some reason, when flipping the UVs on the sprite, there is a light "bleeding" or whatever you call it where the UV tries to sample past the texture boundry and ends up clipping. This is only noticed when resizing the window, though. You can see a screenshot below. ![image](https://user-images.githubusercontent.com/25393315/107098172-397aaa00-67d4-11eb-8e02-c90c820cd70e.png) I am quite baffled why the texture sampling is overrunning like it is and could use some guidance if anybody knows what might be wrong. The other issue, which I just worked around, is that I had to remove the `#[render_resources(from_self)]` annotation from the Spritesheet because the `SpriteFlip` render resource wasn't being picked up properly in the shader when using it. I'm not sure what the cause of that was, but by removing the annotation and re-organizing the shader inputs accordingly the problem was fixed. I'm not sure if this is the most efficient way to do this or if there is a better way, but I wanted to try it out if only for the learning experience. Let me know what you think!
2021-03-03 19:26:45 +00:00
pub flip_x: bool,
pub flip_y: bool,
/// An optional custom size for the sprite that will be used when rendering, instead of the size
/// of the sprite's image in the atlas
pub custom_size: Option<Vec2>,
pub anchor: Anchor,
Add Sprite Flipping (#1407) OK, here's my attempt at sprite flipping. There are a couple of points that I need review/help on, but I think the UX is about ideal: ```rust .spawn(SpriteBundle { material: materials.add(texture_handle.into()), sprite: Sprite { // Flip the sprite along the x axis flip: SpriteFlip { x: true, y: false }, ..Default::default() }, ..Default::default() }); ``` Now for the issues. The big issue is that for some reason, when flipping the UVs on the sprite, there is a light "bleeding" or whatever you call it where the UV tries to sample past the texture boundry and ends up clipping. This is only noticed when resizing the window, though. You can see a screenshot below. ![image](https://user-images.githubusercontent.com/25393315/107098172-397aaa00-67d4-11eb-8e02-c90c820cd70e.png) I am quite baffled why the texture sampling is overrunning like it is and could use some guidance if anybody knows what might be wrong. The other issue, which I just worked around, is that I had to remove the `#[render_resources(from_self)]` annotation from the Spritesheet because the `SpriteFlip` render resource wasn't being picked up properly in the shader when using it. I'm not sure what the cause of that was, but by removing the annotation and re-organizing the shader inputs accordingly the problem was fixed. I'm not sure if this is the most efficient way to do this or if there is a better way, but I wanted to try it out if only for the learning experience. Let me know what you think!
2021-03-03 19:26:45 +00:00
}
impl Default for TextureAtlasSprite {
fn default() -> Self {
Self {
index: 0,
color: Color::WHITE,
Add Sprite Flipping (#1407) OK, here's my attempt at sprite flipping. There are a couple of points that I need review/help on, but I think the UX is about ideal: ```rust .spawn(SpriteBundle { material: materials.add(texture_handle.into()), sprite: Sprite { // Flip the sprite along the x axis flip: SpriteFlip { x: true, y: false }, ..Default::default() }, ..Default::default() }); ``` Now for the issues. The big issue is that for some reason, when flipping the UVs on the sprite, there is a light "bleeding" or whatever you call it where the UV tries to sample past the texture boundry and ends up clipping. This is only noticed when resizing the window, though. You can see a screenshot below. ![image](https://user-images.githubusercontent.com/25393315/107098172-397aaa00-67d4-11eb-8e02-c90c820cd70e.png) I am quite baffled why the texture sampling is overrunning like it is and could use some guidance if anybody knows what might be wrong. The other issue, which I just worked around, is that I had to remove the `#[render_resources(from_self)]` annotation from the Spritesheet because the `SpriteFlip` render resource wasn't being picked up properly in the shader when using it. I'm not sure what the cause of that was, but by removing the annotation and re-organizing the shader inputs accordingly the problem was fixed. I'm not sure if this is the most efficient way to do this or if there is a better way, but I wanted to try it out if only for the learning experience. Let me know what you think!
2021-03-03 19:26:45 +00:00
flip_x: false,
flip_y: false,
custom_size: None,
anchor: Anchor::default(),
}
}
}
impl TextureAtlasSprite {
pub fn new(index: usize) -> TextureAtlasSprite {
Self {
index,
..Default::default()
}
}
}
2020-06-06 07:12:38 +00:00
impl TextureAtlas {
2020-08-25 00:57:10 +00:00
/// Create a new `TextureAtlas` that has a texture, but does not have
/// any individual sprites specified
pub fn new_empty(texture: Handle<Image>, dimensions: Vec2) -> Self {
Self {
texture,
size: dimensions,
texture_handles: None,
textures: Vec::new(),
}
}
2020-08-25 00:57:10 +00:00
/// Generate a `TextureAtlas` by splitting a texture into a grid where each
/// `tile_size` by `tile_size` grid-cell is one of the textures in the atlas
2020-06-04 02:00:19 +00:00
pub fn from_grid(
texture: Handle<Image>,
tile_size: Vec2,
2020-06-04 02:00:19 +00:00
columns: usize,
rows: usize,
2020-06-06 07:12:38 +00:00
) -> TextureAtlas {
Self::from_grid_with_padding(texture, tile_size, columns, rows, Vec2::ZERO, Vec2::ZERO)
}
/// Generate a `TextureAtlas` by splitting a texture into a grid where each
/// `tile_size` by `tile_size` grid-cell is one of the textures in the
/// atlas. Grid cells are separated by some `padding`, and the grid starts
/// at `offset` pixels from the top left corner.
pub fn from_grid_with_padding(
texture: Handle<Image>,
tile_size: Vec2,
columns: usize,
rows: usize,
padding: Vec2,
offset: Vec2,
) -> TextureAtlas {
2020-06-04 02:00:19 +00:00
let mut sprites = Vec::new();
let mut x_padding = 0.0;
let mut y_padding = 0.0;
2020-06-04 02:00:19 +00:00
for y in 0..rows {
if y > 0 {
y_padding = padding.y;
}
2020-06-04 02:00:19 +00:00
for x in 0..columns {
if x > 0 {
x_padding = padding.x;
}
let rect_min = Vec2::new(
(tile_size.x + x_padding) * x as f32 + offset.x,
(tile_size.y + y_padding) * y as f32 + offset.y,
);
2020-06-04 02:00:19 +00:00
sprites.push(Rect {
min: rect_min,
max: Vec2::new(rect_min.x + tile_size.x, rect_min.y + tile_size.y),
});
}
}
2020-06-06 07:12:38 +00:00
TextureAtlas {
size: Vec2::new(
((tile_size.x + x_padding) * columns as f32) - x_padding,
((tile_size.y + y_padding) * rows as f32) - y_padding,
),
2020-06-06 07:12:38 +00:00
textures: sprites,
2020-06-04 02:00:19 +00:00
texture,
2020-06-06 07:12:38 +00:00
texture_handles: None,
}
2020-06-04 02:00:19 +00:00
}
2020-06-06 07:12:38 +00:00
2020-08-25 00:57:10 +00:00
/// Add a sprite to the list of textures in the `TextureAtlas`
/// returns an index to the texture which can be used with `TextureAtlasSprite`
2020-08-25 00:57:10 +00:00
///
/// # Arguments
///
/// * `rect` - The section of the atlas that contains the texture to be added,
/// from the top-left corner of the texture to the bottom-right corner
pub fn add_texture(&mut self, rect: Rect) -> usize {
self.textures.push(rect);
self.textures.len() - 1
}
2020-08-25 00:57:10 +00:00
/// How many textures are in the `TextureAtlas`
pub fn len(&self) -> usize {
self.textures.len()
}
pub fn is_empty(&self) -> bool {
self.textures.is_empty()
}
pub fn get_texture_index(&self, texture: &Handle<Image>) -> Option<usize> {
2020-06-06 07:12:38 +00:00
self.texture_handles
.as_ref()
.and_then(|texture_handles| texture_handles.get(texture).cloned())
2020-06-06 07:12:38 +00:00
}
2020-07-10 08:37:06 +00:00
}