-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add sprite atlases into the new renderer. (#2560)
# Objective Restore the functionality of sprite atlases in the new renderer. ### **Note:** This PR relies on #2555 ## Solution Mostly just a copy paste of the existing sprite atlas implementation, however I unified the rendering between sprites and atlases. Co-authored-by: Carter Anderson <[email protected]>
- Loading branch information
Showing
11 changed files
with
708 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use bevy::{ | ||
asset::LoadState, | ||
math::{Vec2, Vec3}, | ||
prelude::{ | ||
App, AssetServer, Assets, Commands, HandleUntyped, IntoSystem, Res, ResMut, State, | ||
SystemSet, Transform, | ||
}, | ||
render2::{camera::OrthographicCameraBundle, texture::Image}, | ||
sprite2::{ | ||
PipelinedSpriteBundle, PipelinedSpriteSheetBundle, Sprite, TextureAtlas, | ||
TextureAtlasBuilder, TextureAtlasSprite, | ||
}, | ||
PipelinedDefaultPlugins, | ||
}; | ||
|
||
/// In this example we generate a new texture atlas (sprite sheet) from a folder containing | ||
/// individual sprites | ||
fn main() { | ||
App::new() | ||
.init_resource::<RpgSpriteHandles>() | ||
.add_plugins(PipelinedDefaultPlugins) | ||
.add_state(AppState::Setup) | ||
.add_system_set(SystemSet::on_enter(AppState::Setup).with_system(load_textures.system())) | ||
.add_system_set(SystemSet::on_update(AppState::Setup).with_system(check_textures.system())) | ||
.add_system_set(SystemSet::on_enter(AppState::Finished).with_system(setup.system())) | ||
.run(); | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
enum AppState { | ||
Setup, | ||
Finished, | ||
} | ||
|
||
#[derive(Default)] | ||
struct RpgSpriteHandles { | ||
handles: Vec<HandleUntyped>, | ||
} | ||
|
||
fn load_textures(mut rpg_sprite_handles: ResMut<RpgSpriteHandles>, asset_server: Res<AssetServer>) { | ||
rpg_sprite_handles.handles = asset_server.load_folder("textures/rpg").unwrap(); | ||
} | ||
|
||
fn check_textures( | ||
mut state: ResMut<State<AppState>>, | ||
rpg_sprite_handles: ResMut<RpgSpriteHandles>, | ||
asset_server: Res<AssetServer>, | ||
) { | ||
if let LoadState::Loaded = | ||
asset_server.get_group_load_state(rpg_sprite_handles.handles.iter().map(|handle| handle.id)) | ||
{ | ||
state.set(AppState::Finished).unwrap(); | ||
} | ||
} | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
rpg_sprite_handles: Res<RpgSpriteHandles>, | ||
asset_server: Res<AssetServer>, | ||
mut texture_atlases: ResMut<Assets<TextureAtlas>>, | ||
mut textures: ResMut<Assets<Image>>, | ||
) { | ||
let mut texture_atlas_builder = TextureAtlasBuilder::default(); | ||
for handle in rpg_sprite_handles.handles.iter() { | ||
let texture = textures.get(handle).unwrap(); | ||
texture_atlas_builder.add_texture(handle.clone_weak().typed::<Image>(), texture); | ||
} | ||
|
||
let texture_atlas = texture_atlas_builder.finish(&mut textures).unwrap(); | ||
let texture_atlas_texture = texture_atlas.texture.clone(); | ||
let vendor_handle = asset_server.get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png"); | ||
let vendor_index = texture_atlas.get_texture_index(&vendor_handle).unwrap(); | ||
let atlas_handle = texture_atlases.add(texture_atlas); | ||
|
||
// set up a scene to display our texture atlas | ||
commands.spawn_bundle(OrthographicCameraBundle::new_2d()); | ||
// draw a sprite from the atlas | ||
commands.spawn_bundle(PipelinedSpriteSheetBundle { | ||
transform: Transform { | ||
translation: Vec3::new(150.0, 0.0, 0.0), | ||
scale: Vec3::splat(4.0), | ||
..Default::default() | ||
}, | ||
sprite: TextureAtlasSprite::new(vendor_index as u32), | ||
texture_atlas: atlas_handle, | ||
..Default::default() | ||
}); | ||
// draw the atlas itself | ||
commands.spawn_bundle(PipelinedSpriteBundle { | ||
sprite: Sprite { | ||
size: Vec2::new(512.0, 512.0), | ||
..Default::default() | ||
}, | ||
texture: texture_atlas_texture, | ||
transform: Transform::from_xyz(-300.0, 0.0, 0.0), | ||
..Default::default() | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
pipelined/bevy_sprite2/src/dynamic_texture_atlas_builder.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
use crate::{Rect, TextureAtlas}; | ||
use bevy_asset::Assets; | ||
use bevy_math::Vec2; | ||
use bevy_render2::texture::{Image, TextureFormatPixelInfo}; | ||
use guillotiere::{size2, Allocation, AtlasAllocator}; | ||
|
||
pub struct DynamicTextureAtlasBuilder { | ||
pub atlas_allocator: AtlasAllocator, | ||
pub padding: i32, | ||
} | ||
|
||
impl DynamicTextureAtlasBuilder { | ||
pub fn new(size: Vec2, padding: i32) -> Self { | ||
Self { | ||
atlas_allocator: AtlasAllocator::new(to_size2(size)), | ||
padding, | ||
} | ||
} | ||
|
||
pub fn add_texture( | ||
&mut self, | ||
texture_atlas: &mut TextureAtlas, | ||
textures: &mut Assets<Image>, | ||
texture: &Image, | ||
) -> Option<u32> { | ||
let allocation = self.atlas_allocator.allocate(size2( | ||
texture.texture_descriptor.size.width as i32 + self.padding, | ||
texture.texture_descriptor.size.height as i32 + self.padding, | ||
)); | ||
if let Some(allocation) = allocation { | ||
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap(); | ||
self.place_texture(atlas_texture, allocation, texture); | ||
let mut rect: Rect = allocation.rectangle.into(); | ||
rect.max.x -= self.padding as f32; | ||
rect.max.y -= self.padding as f32; | ||
texture_atlas.add_texture(rect); | ||
Some((texture_atlas.len() - 1) as u32) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
// fn resize( | ||
// &mut self, | ||
// texture_atlas: &mut TextureAtlas, | ||
// textures: &mut Assets<Texture>, | ||
// size: Vec2, | ||
// ) { | ||
// let new_size2 = to_size2(new_size); | ||
// self.atlas_texture = Texture::new_fill(new_size, &[0,0,0,0]); | ||
// let change_list = self.atlas_allocator.resize_and_rearrange(new_size2); | ||
|
||
// for change in change_list.changes { | ||
// if let Some(changed_texture_handle) = self.allocation_textures.remove(&change.old.id) | ||
// { let changed_texture = textures.get(&changed_texture_handle).unwrap(); | ||
// self.place_texture(change.new, changed_texture_handle, changed_texture); | ||
// } | ||
// } | ||
|
||
// for failure in change_list.failures { | ||
// let failed_texture = self.allocation_textures.remove(&failure.id).unwrap(); | ||
// queued_textures.push(failed_texture); | ||
// } | ||
// } | ||
|
||
fn place_texture( | ||
&mut self, | ||
atlas_texture: &mut Image, | ||
allocation: Allocation, | ||
texture: &Image, | ||
) { | ||
let mut rect = allocation.rectangle; | ||
rect.max.x -= self.padding; | ||
rect.max.y -= self.padding; | ||
let atlas_width = atlas_texture.texture_descriptor.size.width as usize; | ||
let rect_width = rect.width() as usize; | ||
let format_size = atlas_texture.texture_descriptor.format.pixel_size(); | ||
|
||
for (texture_y, bound_y) in (rect.min.y..rect.max.y).map(|i| i as usize).enumerate() { | ||
let begin = (bound_y * atlas_width + rect.min.x as usize) * format_size; | ||
let end = begin + rect_width * format_size; | ||
let texture_begin = texture_y * rect_width * format_size; | ||
let texture_end = texture_begin + rect_width * format_size; | ||
atlas_texture.data[begin..end] | ||
.copy_from_slice(&texture.data[texture_begin..texture_end]); | ||
} | ||
} | ||
} | ||
|
||
impl From<guillotiere::Rectangle> for Rect { | ||
fn from(rectangle: guillotiere::Rectangle) -> Self { | ||
Rect { | ||
min: Vec2::new(rectangle.min.x as f32, rectangle.min.y as f32), | ||
max: Vec2::new(rectangle.max.x as f32, rectangle.max.y as f32), | ||
} | ||
} | ||
} | ||
|
||
fn to_size2(vec2: Vec2) -> guillotiere::Size { | ||
guillotiere::Size::new(vec2.x as i32, vec2.y as i32) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.