2022-09-02 12:35:23 +00:00
|
|
|
use crate::Anchor;
|
2020-06-04 02:00:19 +00:00
|
|
|
use bevy_asset::Handle;
|
2021-10-03 19:23:44 +00:00
|
|
|
use bevy_ecs::component::Component;
|
2022-09-02 12:35:23 +00:00
|
|
|
use bevy_math::{Rect, Vec2};
|
2021-12-14 03:58:23 +00:00
|
|
|
use bevy_reflect::{Reflect, TypeUuid};
|
|
|
|
use bevy_render::{color::Color, texture::Image};
|
2020-08-29 00:08:51 +00:00
|
|
|
use bevy_utils::HashMap;
|
2020-06-01 06:39:20 +00:00
|
|
|
|
2021-05-05 18:45:49 +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)
|
2021-12-14 03:58:23 +00:00
|
|
|
#[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
|
2021-12-14 03:58:23 +00:00
|
|
|
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
|
2020-06-14 01:53:31 +00:00
|
|
|
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>,
|
2021-12-14 03:58:23 +00:00
|
|
|
pub texture_handles: Option<HashMap<Handle<Image>, usize>>,
|
2020-06-02 02:23:11 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 19:58:02 +00:00
|
|
|
#[derive(Component, Debug, Clone, Reflect)]
|
2020-06-06 07:12:38 +00:00
|
|
|
pub struct TextureAtlasSprite {
|
2020-06-22 00:43:36 +00:00
|
|
|
pub color: Color,
|
2021-12-14 03:58:23 +00:00
|
|
|
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,
|
Add 2d meshes and materials (#3460)
# Objective
The current 2d rendering is specialized to render sprites, we need a generic way to render 2d items, using meshes and materials like we have for 3d.
## Solution
I cloned a good part of `bevy_pbr` into `bevy_sprite/src/mesh2d`, removed lighting and pbr itself, adapted it to 2d rendering, added a `ColorMaterial`, and modified the sprite rendering to break batches around 2d meshes.
~~The PR is a bit crude; I tried to change as little as I could in both the parts copied from 3d and the current sprite rendering to make reviewing easier. In the future, I expect we could make the sprite rendering a normal 2d material, cleanly integrated with the rest.~~ _edit: see <https://github.com/bevyengine/bevy/pull/3460#issuecomment-1003605194>_
## Remaining work
- ~~don't require mesh normals~~ _out of scope_
- ~~add an example~~ _done_
- support 2d meshes & materials in the UI?
- bikeshed names (I didn't think hard about naming, please check if it's fine)
## Remaining questions
- ~~should we add a depth buffer to 2d now that there are 2d meshes?~~ _let's revisit that when we have an opaque render phase_
- ~~should we add MSAA support to the sprites, or remove it from the 2d meshes?~~ _I added MSAA to sprites since it's really needed for 2d meshes_
- ~~how to customize vertex attributes?~~ _#3120_
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-01-08 01:29:08 +00:00
|
|
|
/// 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>,
|
2022-04-04 22:09:59 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-06-22 00:43:36 +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,
|
Add 2d meshes and materials (#3460)
# Objective
The current 2d rendering is specialized to render sprites, we need a generic way to render 2d items, using meshes and materials like we have for 3d.
## Solution
I cloned a good part of `bevy_pbr` into `bevy_sprite/src/mesh2d`, removed lighting and pbr itself, adapted it to 2d rendering, added a `ColorMaterial`, and modified the sprite rendering to break batches around 2d meshes.
~~The PR is a bit crude; I tried to change as little as I could in both the parts copied from 3d and the current sprite rendering to make reviewing easier. In the future, I expect we could make the sprite rendering a normal 2d material, cleanly integrated with the rest.~~ _edit: see <https://github.com/bevyengine/bevy/pull/3460#issuecomment-1003605194>_
## Remaining work
- ~~don't require mesh normals~~ _out of scope_
- ~~add an example~~ _done_
- support 2d meshes & materials in the UI?
- bikeshed names (I didn't think hard about naming, please check if it's fine)
## Remaining questions
- ~~should we add a depth buffer to 2d now that there are 2d meshes?~~ _let's revisit that when we have an opaque render phase_
- ~~should we add MSAA support to the sprites, or remove it from the 2d meshes?~~ _I added MSAA to sprites since it's really needed for 2d meshes_
- ~~how to customize vertex attributes?~~ _#3120_
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-01-08 01:29:08 +00:00
|
|
|
custom_size: None,
|
2022-04-04 22:09:59 +00:00
|
|
|
anchor: Anchor::default(),
|
2020-06-22 19:35:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextureAtlasSprite {
|
2021-12-14 03:58:23 +00:00
|
|
|
pub fn new(index: usize) -> TextureAtlasSprite {
|
2020-06-22 19:35:33 +00:00
|
|
|
Self {
|
|
|
|
index,
|
|
|
|
..Default::default()
|
2020-06-22 00:43:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
2021-12-14 03:58:23 +00:00
|
|
|
pub fn new_empty(texture: Handle<Image>, dimensions: Vec2) -> Self {
|
2020-06-14 01:53:31 +00:00
|
|
|
Self {
|
|
|
|
texture,
|
|
|
|
size: dimensions,
|
|
|
|
texture_handles: None,
|
|
|
|
textures: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-15 03:49:07 +00:00
|
|
|
/// Generate a `TextureAtlas` by splitting a texture into a grid where each
|
Added `offset` parameter to `TextureAtlas::from_grid_with_padding` (#4836)
# Objective
Increase compatibility with a fairly common format of padded spritesheets, in which half the padding value occurs before the first sprite box begins. The original behaviour falls out when `Vec2::ZERO` is used for `offset`.
See below unity screenshot for an example of a spritesheet with padding
![Screen Shot 2022-05-24 at 4 11 49 PM](https://user-images.githubusercontent.com/30442265/170123682-287e5733-b69d-452b-b2e6-46d8d29293fb.png)
## Solution
Tiny change to `crates/bevy_sprite/src/texture_atlas.rs`
## Migration Guide
Calls to `TextureAtlas::from_grid_with_padding` should be modified to include a new parameter, which can be set to `Vec2::ZERO` to retain old behaviour.
```rust
from_grid_with_padding(texture, tile_size, columns, rows, padding)
|
V
from_grid_with_padding(texture, tile_size, columns, rows, padding, Vec2::ZERO)
```
Co-authored-by: FraserLee <30442265+FraserLee@users.noreply.github.com>
2022-05-30 19:58:16 +00:00
|
|
|
/// `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
|
2022-09-22 17:44:24 +00:00
|
|
|
/// at `offset` pixels from the top left corner. Resulting `TextureAtlas` is
|
|
|
|
/// indexed left to right, top to bottom.
|
Merge TextureAtlas::from_grid_with_padding into TextureAtlas::from_grid through option arguments (#6057)
This is an adoption of #3775
This merges `TextureAtlas` `from_grid_with_padding` into `from_grid` , adding optional padding and optional offset.
Since the orignal PR, the offset had already been added to from_grid_with_padding through #4836
## Changelog
- Added `padding` and `offset` arguments to `TextureAtlas::from_grid`
- Removed `TextureAtlas::from_grid_with_padding`
## Migration Guide
`TextureAtlas::from_grid_with_padding` was merged into `from_grid` which takes two additional parameters for padding and an offset.
```
// 0.8
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1);
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, None, None)
// 0.8
TextureAtlas::from_grid_with_padding(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Vec2::new(4.0, 4.0));
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Some(Vec2::new(4.0, 4.0)), None)
```
Co-authored-by: olefish <88390729+oledfish@users.noreply.github.com>
2022-09-24 12:58:06 +00:00
|
|
|
pub fn from_grid(
|
2021-12-14 03:58:23 +00:00
|
|
|
texture: Handle<Image>,
|
2020-10-15 03:49:07 +00:00
|
|
|
tile_size: Vec2,
|
|
|
|
columns: usize,
|
|
|
|
rows: usize,
|
Merge TextureAtlas::from_grid_with_padding into TextureAtlas::from_grid through option arguments (#6057)
This is an adoption of #3775
This merges `TextureAtlas` `from_grid_with_padding` into `from_grid` , adding optional padding and optional offset.
Since the orignal PR, the offset had already been added to from_grid_with_padding through #4836
## Changelog
- Added `padding` and `offset` arguments to `TextureAtlas::from_grid`
- Removed `TextureAtlas::from_grid_with_padding`
## Migration Guide
`TextureAtlas::from_grid_with_padding` was merged into `from_grid` which takes two additional parameters for padding and an offset.
```
// 0.8
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1);
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, None, None)
// 0.8
TextureAtlas::from_grid_with_padding(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Vec2::new(4.0, 4.0));
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Some(Vec2::new(4.0, 4.0)), None)
```
Co-authored-by: olefish <88390729+oledfish@users.noreply.github.com>
2022-09-24 12:58:06 +00:00
|
|
|
padding: Option<Vec2>,
|
|
|
|
offset: Option<Vec2>,
|
2020-10-15 03:49:07 +00:00
|
|
|
) -> TextureAtlas {
|
Merge TextureAtlas::from_grid_with_padding into TextureAtlas::from_grid through option arguments (#6057)
This is an adoption of #3775
This merges `TextureAtlas` `from_grid_with_padding` into `from_grid` , adding optional padding and optional offset.
Since the orignal PR, the offset had already been added to from_grid_with_padding through #4836
## Changelog
- Added `padding` and `offset` arguments to `TextureAtlas::from_grid`
- Removed `TextureAtlas::from_grid_with_padding`
## Migration Guide
`TextureAtlas::from_grid_with_padding` was merged into `from_grid` which takes two additional parameters for padding and an offset.
```
// 0.8
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1);
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, None, None)
// 0.8
TextureAtlas::from_grid_with_padding(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Vec2::new(4.0, 4.0));
// 0.9
TextureAtlas::from_grid(texture_handle, Vec2::new(24.0, 24.0), 7, 1, Some(Vec2::new(4.0, 4.0)), None)
```
Co-authored-by: olefish <88390729+oledfish@users.noreply.github.com>
2022-09-24 12:58:06 +00:00
|
|
|
let padding = padding.unwrap_or_default();
|
|
|
|
let offset = offset.unwrap_or_default();
|
2020-06-04 02:00:19 +00:00
|
|
|
let mut sprites = Vec::new();
|
2022-09-02 12:35:23 +00:00
|
|
|
let mut current_padding = Vec2::ZERO;
|
2020-10-15 03:49:07 +00:00
|
|
|
|
2020-06-04 02:00:19 +00:00
|
|
|
for y in 0..rows {
|
2020-10-15 03:49:07 +00:00
|
|
|
if y > 0 {
|
2022-09-02 12:35:23 +00:00
|
|
|
current_padding.y = padding.y;
|
2020-10-15 03:49:07 +00:00
|
|
|
}
|
2020-06-04 02:00:19 +00:00
|
|
|
for x in 0..columns {
|
2020-10-15 03:49:07 +00:00
|
|
|
if x > 0 {
|
2022-09-02 12:35:23 +00:00
|
|
|
current_padding.x = padding.x;
|
2020-10-15 03:49:07 +00:00
|
|
|
}
|
|
|
|
|
2022-09-02 12:35:23 +00:00
|
|
|
let cell = Vec2::new(x as f32, y as f32);
|
|
|
|
|
|
|
|
let rect_min = (tile_size + current_padding) * cell + offset;
|
2020-10-15 03:49:07 +00:00
|
|
|
|
2020-06-04 02:00:19 +00:00
|
|
|
sprites.push(Rect {
|
2020-10-15 03:49:07 +00:00
|
|
|
min: rect_min,
|
2022-09-02 12:35:23 +00:00
|
|
|
max: rect_min + tile_size,
|
2022-02-13 22:33:55 +00:00
|
|
|
});
|
2020-06-02 02:23:11 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-15 03:49:07 +00:00
|
|
|
|
2022-09-02 12:35:23 +00:00
|
|
|
let grid_size = Vec2::new(columns as f32, rows as f32);
|
|
|
|
|
2020-06-06 07:12:38 +00:00
|
|
|
TextureAtlas {
|
2022-09-02 12:35:23 +00:00
|
|
|
size: ((tile_size + current_padding) * grid_size) - current_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-02 02:23:11 +00:00
|
|
|
}
|
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`
|
add_texture returns index to texture (#2864)
If you need to build a texture atlas from an already created texture that is not match a grid, you need to use new_empty and add_texture to create it. However it is not straight forward to get the index to be used with TextureAtlasSprite. add_texture should be changed to return the index to the texture.
Currently you can do something like this:
```rs
let texture = asset_server.load::<Texture>::("texture.png");
let texture_atlas = TextureAtlas::new_empty(texture, Vec2::new(40.0, 40.0));
texture_atlas.add_texture(Rect {
min: Vec2::new(20.0, 20.0),
max: Vec2::new(40.0, 40.0),
});
let index = (texture_atlas.len() - 1) as u32;
let texture_atlas_sprite = TextureAtlasSprite {
index,
Default::default()
};
```
But this is more clear
```rs
let index = texture_atlas.add_texture(Rect {
min: Vec2::new(20.0, 20.0),
max: Vec2::new(40.0, 40.0),
});
```
2021-09-28 20:54:16 +00:00
|
|
|
/// 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
|
2021-12-14 03:58:23 +00:00
|
|
|
pub fn add_texture(&mut self, rect: Rect) -> usize {
|
2020-06-14 01:53:31 +00:00
|
|
|
self.textures.push(rect);
|
2021-12-14 03:58:23 +00:00
|
|
|
self.textures.len() - 1
|
2020-06-14 01:53:31 +00:00
|
|
|
}
|
|
|
|
|
2020-08-25 00:57:10 +00:00
|
|
|
/// How many textures are in the `TextureAtlas`
|
2020-06-14 01:53:31 +00:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.textures.len()
|
|
|
|
}
|
|
|
|
|
2020-08-16 07:30:04 +00:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.textures.is_empty()
|
|
|
|
}
|
|
|
|
|
2021-12-14 03:58:23 +00:00
|
|
|
pub fn get_texture_index(&self, texture: &Handle<Image>) -> Option<usize> {
|
2020-06-06 07:12:38 +00:00
|
|
|
self.texture_handles
|
|
|
|
.as_ref()
|
2020-10-18 20:48:15 +00:00
|
|
|
.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
|
|
|
}
|