mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
add texture atlases
This commit is contained in:
parent
ffc4246a74
commit
2705e5cbb4
16 changed files with 242 additions and 57 deletions
|
@ -64,6 +64,10 @@ path = "examples/2d/sprite.rs"
|
||||||
name = "sprite_sheet"
|
name = "sprite_sheet"
|
||||||
path = "examples/2d/sprite_sheet.rs"
|
path = "examples/2d/sprite_sheet.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "texture_atlas"
|
||||||
|
path = "examples/2d/texture_atlas.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "load_model"
|
name = "load_model"
|
||||||
path = "examples/3d/load_model.rs"
|
path = "examples/3d/load_model.rs"
|
||||||
|
|
|
@ -46,6 +46,7 @@ fn get_primitive_topology(mode: Mode) -> Result<PrimitiveTopology, GltfError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this should return a scene
|
||||||
pub fn load_gltf(asset_path: &Path, bytes: Vec<u8>) -> Result<Mesh, GltfError> {
|
pub fn load_gltf(asset_path: &Path, bytes: Vec<u8>) -> Result<Mesh, GltfError> {
|
||||||
let gltf = gltf::Gltf::from_slice(&bytes)?;
|
let gltf = gltf::Gltf::from_slice(&bytes)?;
|
||||||
let buffer_data = load_buffers(gltf.buffers(), asset_path)?;
|
let buffer_data = load_buffers(gltf.buffers(), asset_path)?;
|
||||||
|
|
|
@ -36,6 +36,7 @@ impl Default for PerspectiveProjection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: make this a component instead of a property
|
||||||
#[derive(Debug, Clone, Property, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Property, Serialize, Deserialize)]
|
||||||
pub enum WindowOrigin {
|
pub enum WindowOrigin {
|
||||||
Center,
|
Center,
|
||||||
|
|
|
@ -91,7 +91,10 @@ impl AppPlugin for RenderPlugin {
|
||||||
.init_resource::<EntityRenderResourceAssignments>()
|
.init_resource::<EntityRenderResourceAssignments>()
|
||||||
.init_resource::<EntitiesWaitingForAssets>()
|
.init_resource::<EntitiesWaitingForAssets>()
|
||||||
.init_resource::<TextureResourceSystemState>()
|
.init_resource::<TextureResourceSystemState>()
|
||||||
.add_system(entity_render_resource_assignments_system())
|
.add_system_to_stage(
|
||||||
|
bevy_app::stage::POST_UPDATE,
|
||||||
|
entity_render_resource_assignments_system(),
|
||||||
|
)
|
||||||
.init_system_to_stage(
|
.init_system_to_stage(
|
||||||
bevy_app::stage::POST_UPDATE,
|
bevy_app::stage::POST_UPDATE,
|
||||||
camera::camera_system::<OrthographicProjection>,
|
camera::camera_system::<OrthographicProjection>,
|
||||||
|
|
|
@ -14,5 +14,7 @@ bevy_type_registry = { path = "../bevy_type_registry" }
|
||||||
bevy_derive = { path = "../bevy_derive" }
|
bevy_derive = { path = "../bevy_derive" }
|
||||||
bevy_render = { path = "../bevy_render" }
|
bevy_render = { path = "../bevy_render" }
|
||||||
bevy_transform = { path = "../bevy_transform" }
|
bevy_transform = { path = "../bevy_transform" }
|
||||||
|
|
||||||
|
legion = { path = "../bevy_legion", features = ["serialize"] }
|
||||||
glam = "0.8.7"
|
glam = "0.8.7"
|
||||||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
guillotiere = "0.5.2"
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
render::SPRITE_PIPELINE_HANDLE, sprite::Sprite, ColorMaterial, Quad, SpriteSheet,
|
render::SPRITE_PIPELINE_HANDLE, sprite::Sprite, ColorMaterial, Quad, TextureAtlas,
|
||||||
SpriteSheetSprite, QUAD_HANDLE, SPRITE_SHEET_PIPELINE_HANDLE,
|
TextureAtlasSprite, QUAD_HANDLE, SPRITE_SHEET_PIPELINE_HANDLE,
|
||||||
};
|
};
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_derive::EntityArchetype;
|
use bevy_derive::EntityArchetype;
|
||||||
|
@ -32,8 +32,8 @@ impl Default for SpriteEntity {
|
||||||
|
|
||||||
#[derive(EntityArchetype)]
|
#[derive(EntityArchetype)]
|
||||||
pub struct SpriteSheetEntity {
|
pub struct SpriteSheetEntity {
|
||||||
pub sprite: SpriteSheetSprite,
|
pub sprite: TextureAtlasSprite,
|
||||||
pub sprite_sheet: Handle<SpriteSheet>,
|
pub texture_atlas: Handle<TextureAtlas>,
|
||||||
pub renderable: Renderable,
|
pub renderable: Renderable,
|
||||||
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
|
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
|
||||||
// pub local_to_world: LocalToWorld,
|
// pub local_to_world: LocalToWorld,
|
||||||
|
@ -46,7 +46,7 @@ impl Default for SpriteSheetEntity {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
sprite: Default::default(),
|
sprite: Default::default(),
|
||||||
sprite_sheet: Default::default(),
|
texture_atlas: Default::default(),
|
||||||
renderable: Renderable {
|
renderable: Renderable {
|
||||||
pipelines: vec![SPRITE_SHEET_PIPELINE_HANDLE],
|
pipelines: vec![SPRITE_SHEET_PIPELINE_HANDLE],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -4,14 +4,16 @@ mod quad;
|
||||||
mod rect;
|
mod rect;
|
||||||
mod render;
|
mod render;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod sprite_sheet;
|
mod texture_atlas;
|
||||||
|
mod texture_atlas_builder;
|
||||||
|
|
||||||
pub use color_material::*;
|
pub use color_material::*;
|
||||||
pub use quad::*;
|
pub use quad::*;
|
||||||
pub use rect::*;
|
pub use rect::*;
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
pub use sprite::*;
|
pub use sprite::*;
|
||||||
pub use sprite_sheet::*;
|
pub use texture_atlas::*;
|
||||||
|
pub use texture_atlas_builder::*;
|
||||||
|
|
||||||
use bevy_app::{stage, AppBuilder, AppPlugin};
|
use bevy_app::{stage, AppBuilder, AppPlugin};
|
||||||
use bevy_asset::{AddAsset, Assets, Handle};
|
use bevy_asset::{AddAsset, Assets, Handle};
|
||||||
|
@ -32,7 +34,7 @@ pub const QUAD_HANDLE: Handle<Mesh> = Handle::from_u128(142404619811301375266013
|
||||||
impl AppPlugin for SpritePlugin {
|
impl AppPlugin for SpritePlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.add_asset::<ColorMaterial>()
|
app.add_asset::<ColorMaterial>()
|
||||||
.add_asset::<SpriteSheet>()
|
.add_asset::<TextureAtlas>()
|
||||||
.add_system_to_stage(stage::POST_UPDATE, sprite_system())
|
.add_system_to_stage(stage::POST_UPDATE, sprite_system())
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
stage::POST_UPDATE,
|
stage::POST_UPDATE,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{ColorMaterial, Quad, SpriteSheet, SpriteSheetSprite};
|
use crate::{ColorMaterial, Quad, TextureAtlas, TextureAtlasSprite};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
base_render_graph,
|
base_render_graph,
|
||||||
|
@ -135,12 +135,12 @@ impl SpriteRenderGraphBuilder for RenderGraph {
|
||||||
|
|
||||||
self.add_system_node(
|
self.add_system_node(
|
||||||
node::SPRITE_SHEET,
|
node::SPRITE_SHEET,
|
||||||
AssetUniformNode::<SpriteSheet>::new(false),
|
AssetUniformNode::<TextureAtlas>::new(false),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.add_system_node(
|
self.add_system_node(
|
||||||
node::SPRITE_SHEET_SPRITE,
|
node::SPRITE_SHEET_SPRITE,
|
||||||
UniformNode::<SpriteSheetSprite>::new(true),
|
UniformNode::<TextureAtlasSprite>::new(true),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
|
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
|
||||||
|
|
|
@ -4,11 +4,11 @@ layout(location = 0) in vec2 v_Uv;
|
||||||
|
|
||||||
layout(location = 0) out vec4 o_Target;
|
layout(location = 0) out vec4 o_Target;
|
||||||
|
|
||||||
layout(set = 1, binding = 2) uniform texture2D SpriteSheet_texture;
|
layout(set = 1, binding = 2) uniform texture2D TextureAtlas_texture;
|
||||||
layout(set = 1, binding = 3) uniform sampler SpriteSheet_texture_sampler;
|
layout(set = 1, binding = 3) uniform sampler TextureAtlas_texture_sampler;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
o_Target = texture(
|
o_Target = texture(
|
||||||
sampler2D(SpriteSheet_texture, SpriteSheet_texture_sampler),
|
sampler2D(TextureAtlas_texture, TextureAtlas_texture_sampler),
|
||||||
v_Uv);
|
v_Uv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,6 @@ layout(location = 0) in vec3 Vertex_Position;
|
||||||
layout(location = 1) in vec3 Vertex_Normal;
|
layout(location = 1) in vec3 Vertex_Normal;
|
||||||
layout(location = 2) in vec2 Vertex_Uv;
|
layout(location = 2) in vec2 Vertex_Uv;
|
||||||
|
|
||||||
// TODO: uncomment when instancing is implemented
|
|
||||||
// sprite
|
|
||||||
// layout(location = 0) in vec3 Sprite_Position;
|
|
||||||
// layout(location = 1) in int Sprite_Index;
|
|
||||||
|
|
||||||
layout(location = 0) out vec2 v_Uv;
|
layout(location = 0) out vec2 v_Uv;
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform Camera2d {
|
layout(set = 0, binding = 0) uniform Camera2d {
|
||||||
|
@ -16,7 +11,7 @@ layout(set = 0, binding = 0) uniform Camera2d {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: merge dimensions into "sprites" buffer when that is supported in the Uniforms derive abstraction
|
// TODO: merge dimensions into "sprites" buffer when that is supported in the Uniforms derive abstraction
|
||||||
layout(set = 1, binding = 0) uniform SpriteSheet_dimensions {
|
layout(set = 1, binding = 0) uniform TextureAtlas_dimensions {
|
||||||
vec2 Dimensions;
|
vec2 Dimensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,21 +20,21 @@ struct Rect {
|
||||||
vec2 end;
|
vec2 end;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 1, binding = 1) buffer SpriteSheet_sprites {
|
layout(set = 1, binding = 1) buffer TextureAtlas_textures {
|
||||||
Rect[] Sprites;
|
Rect[] Textures;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
layout(set = 2, binding = 0) uniform SpriteSheetSprite {
|
layout(set = 2, binding = 0) uniform TextureAtlasSprite {
|
||||||
vec3 SpriteSheetSprite_position;
|
vec3 TextureAtlasSprite_position;
|
||||||
float SpriteSheetSprite_scale;
|
float TextureAtlasSprite_scale;
|
||||||
uint SpriteSheetSprite_index;
|
uint TextureAtlasSprite_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Rect sprite_rect = Sprites[SpriteSheetSprite_index];
|
Rect sprite_rect = Textures[TextureAtlasSprite_index];
|
||||||
vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin;
|
vec2 sprite_dimensions = sprite_rect.end - sprite_rect.begin;
|
||||||
vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions * SpriteSheetSprite_scale, 0.0) + SpriteSheetSprite_position;
|
vec3 vertex_position = vec3(Vertex_Position.xy * sprite_dimensions * TextureAtlasSprite_scale, 0.0) + TextureAtlasSprite_position;
|
||||||
vec2 uvs[4] = vec2[](
|
vec2 uvs[4] = vec2[](
|
||||||
vec2(sprite_rect.begin.x, sprite_rect.end.y),
|
vec2(sprite_rect.begin.x, sprite_rect.end.y),
|
||||||
sprite_rect.begin,
|
sprite_rect.begin,
|
||||||
|
|
|
@ -3,50 +3,60 @@ use bevy_asset::Handle;
|
||||||
use bevy_derive::{Bytes, Uniform, Uniforms};
|
use bevy_derive::{Bytes, Uniform, Uniforms};
|
||||||
use bevy_render::texture::Texture;
|
use bevy_render::texture::Texture;
|
||||||
use glam::{Vec2, Vec3};
|
use glam::{Vec2, Vec3};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Uniforms)]
|
#[derive(Uniforms)]
|
||||||
pub struct SpriteSheet {
|
pub struct TextureAtlas {
|
||||||
pub texture: Handle<Texture>,
|
pub texture: Handle<Texture>,
|
||||||
// TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer
|
// TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer
|
||||||
pub dimensions: Vec2,
|
pub dimensions: Vec2,
|
||||||
#[uniform(buffer)]
|
#[uniform(buffer)]
|
||||||
pub sprites: Vec<Rect>,
|
pub textures: Vec<Rect>,
|
||||||
|
#[uniform(ignore)]
|
||||||
|
pub texture_handles: Option<HashMap<Handle<Texture>, usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: cannot do `unsafe impl Byteable` here because Vec3 takes up the space of a Vec4. If/when glam changes this we can swap out
|
// NOTE: cannot do `unsafe impl Byteable` here because Vec3 takes up the space of a Vec4. If/when glam changes this we can swap out
|
||||||
// Bytes for Byteable as a micro-optimization. https://github.com/bitshifter/glam-rs/issues/36
|
// Bytes for Byteable as a micro-optimization. https://github.com/bitshifter/glam-rs/issues/36
|
||||||
#[derive(Uniform, Bytes, Default)]
|
#[derive(Uniform, Bytes, Default)]
|
||||||
pub struct SpriteSheetSprite {
|
pub struct TextureAtlasSprite {
|
||||||
pub position: Vec3,
|
pub position: Vec3,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpriteSheet {
|
impl TextureAtlas {
|
||||||
pub fn from_grid(
|
pub fn from_grid(
|
||||||
texture: Handle<Texture>,
|
texture: Handle<Texture>,
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
columns: usize,
|
columns: usize,
|
||||||
rows: usize,
|
rows: usize,
|
||||||
) -> SpriteSheet {
|
) -> TextureAtlas {
|
||||||
let sprite_width = size.x() / columns as f32;
|
let texture_width = size.x() / columns as f32;
|
||||||
let sprite_height = size.y() / rows as f32;
|
let texture_height = size.y() / rows as f32;
|
||||||
let mut sprites = Vec::new();
|
let mut sprites = Vec::new();
|
||||||
for y in 0..rows {
|
for y in 0..rows {
|
||||||
for x in 0..columns {
|
for x in 0..columns {
|
||||||
sprites.push(Rect {
|
sprites.push(Rect {
|
||||||
min: Vec2::new(x as f32 * sprite_width, y as f32 * sprite_height),
|
min: Vec2::new(x as f32 * texture_width, y as f32 * texture_height),
|
||||||
max: Vec2::new(
|
max: Vec2::new(
|
||||||
(x + 1) as f32 * sprite_width,
|
(x + 1) as f32 * texture_width,
|
||||||
(y + 1) as f32 * sprite_height,
|
(y + 1) as f32 * texture_height,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpriteSheet {
|
TextureAtlas {
|
||||||
dimensions: size,
|
dimensions: size,
|
||||||
sprites,
|
textures: sprites,
|
||||||
texture,
|
texture,
|
||||||
|
texture_handles: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_texture_index(&self, texture: Handle<Texture>) -> Option<usize> {
|
||||||
|
self.texture_handles
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|texture_handles| texture_handles.get(&texture).cloned())
|
||||||
|
}
|
||||||
}
|
}
|
88
crates/bevy_sprite/src/texture_atlas_builder.rs
Normal file
88
crates/bevy_sprite/src/texture_atlas_builder.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use crate::{TextureAtlas, Rect};
|
||||||
|
use bevy_asset::{Assets, Handle};
|
||||||
|
use bevy_render::texture::Texture;
|
||||||
|
use glam::Vec2;
|
||||||
|
use guillotiere::{size2, Allocation, AtlasAllocator};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct TextureAtlasBuilder {
|
||||||
|
pub texture_allocations: Vec<(Handle<Texture>, Allocation)>,
|
||||||
|
pub atlas_allocator: AtlasAllocator,
|
||||||
|
pub texture: Texture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextureAtlasBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Vec2::new(256., 256.))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FORMAT_SIZE: usize = 4; // TODO: get this from an actual format type
|
||||||
|
impl TextureAtlasBuilder {
|
||||||
|
pub fn new(initial_size: Vec2) -> Self {
|
||||||
|
let width = initial_size.x() as usize;
|
||||||
|
let height = initial_size.y() as usize;
|
||||||
|
Self {
|
||||||
|
texture_allocations: Default::default(),
|
||||||
|
atlas_allocator: AtlasAllocator::new(size2(width as i32, height as i32)),
|
||||||
|
texture: Texture::new(vec![0; width * height * FORMAT_SIZE], initial_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_texture(&mut self, texture_handle: Handle<Texture>, texture: &Texture) {
|
||||||
|
// TODO: resize if allocation fails
|
||||||
|
let allocation = self
|
||||||
|
.atlas_allocator
|
||||||
|
.allocate(size2(texture.size.x() as i32, texture.size.y() as i32))
|
||||||
|
.unwrap();
|
||||||
|
let rect = allocation.rectangle;
|
||||||
|
let atlas_width = self.texture.size.x() as usize;
|
||||||
|
let rect_width = rect.width() as usize;
|
||||||
|
|
||||||
|
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;
|
||||||
|
self.texture.data[begin..end]
|
||||||
|
.copy_from_slice(&texture.data[texture_begin..texture_end]);
|
||||||
|
}
|
||||||
|
self.texture_allocations.push((texture_handle, allocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_texture(&mut self, texture_handle: Handle<Texture>) {
|
||||||
|
if let Some(position) = self.texture_allocations.iter().position(|(handle, _)| *handle == texture_handle) {
|
||||||
|
let (_, allocation) = self.texture_allocations.remove(position);
|
||||||
|
self.atlas_allocator.deallocate(allocation.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(self, textures: &mut Assets<Texture>) -> TextureAtlas {
|
||||||
|
let mut texture_rects = Vec::with_capacity(self.texture_allocations.len());
|
||||||
|
let mut texture_handles = HashMap::with_capacity(self.texture_allocations.len());
|
||||||
|
for (index, (handle, allocation)) in self.texture_allocations.iter().enumerate() {
|
||||||
|
texture_rects.push(allocation.rectangle.into());
|
||||||
|
texture_handles.insert(*handle, index);
|
||||||
|
}
|
||||||
|
TextureAtlas {
|
||||||
|
dimensions: to_vec2(self.atlas_allocator.size()),
|
||||||
|
texture: textures.add(self.texture),
|
||||||
|
textures: texture_rects,
|
||||||
|
texture_handles: Some(texture_handles),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_vec2(size: guillotiere::Size) -> Vec2 {
|
||||||
|
Vec2::new(size.width as f32, size.height as f32)
|
||||||
|
}
|
||||||
|
|
|
@ -9,14 +9,14 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn animate_sprite_system(
|
fn animate_sprite_system(
|
||||||
sprite_sheets: Res<Assets<SpriteSheet>>,
|
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||||
mut timer: ComMut<Timer>,
|
mut timer: ComMut<Timer>,
|
||||||
mut sprite: ComMut<SpriteSheetSprite>,
|
mut sprite: ComMut<TextureAtlasSprite>,
|
||||||
sprite_sheet_handle: Com<Handle<SpriteSheet>>,
|
texture_atlas_handle: Com<Handle<TextureAtlas>>,
|
||||||
) {
|
) {
|
||||||
if timer.finished {
|
if timer.finished {
|
||||||
let sprite_sheet = sprite_sheets.get(&sprite_sheet_handle).unwrap();
|
let texture_atlas = texture_atlases.get(&texture_atlas_handle).unwrap();
|
||||||
sprite.index = ((sprite.index as usize + 1) % sprite_sheet.sprites.len()) as u32;
|
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;
|
||||||
timer.reset();
|
timer.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,21 +25,21 @@ fn setup(
|
||||||
command_buffer: &mut CommandBuffer,
|
command_buffer: &mut CommandBuffer,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
mut textures: ResMut<Assets<Texture>>,
|
mut textures: ResMut<Assets<Texture>>,
|
||||||
mut sprite_sheets: ResMut<Assets<SpriteSheet>>,
|
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
||||||
) {
|
) {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let texture_handle = asset_server
|
let texture_handle = asset_server
|
||||||
.load_sync(&mut textures, "assets/textures/rpg/chars/gabe/gabe-idle-run.png")
|
.load_sync(&mut textures, "assets/textures/rpg/chars/gabe/gabe-idle-run.png")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let texture = textures.get(&texture_handle).unwrap();
|
let texture = textures.get(&texture_handle).unwrap();
|
||||||
let sprite_sheet = SpriteSheet::from_grid(texture_handle, texture.size, 7, 1);
|
let texture_atlas = TextureAtlas::from_grid(texture_handle, texture.size, 7, 1);
|
||||||
let sprite_sheet_handle = sprite_sheets.add(sprite_sheet);
|
let texture_atlas_handle = texture_atlases.add(texture_atlas);
|
||||||
command_buffer
|
command_buffer
|
||||||
.build()
|
.build()
|
||||||
.add_entity(OrthographicCameraEntity::default())
|
.add_entity(OrthographicCameraEntity::default())
|
||||||
.add_entity(SpriteSheetEntity {
|
.add_entity(SpriteSheetEntity {
|
||||||
sprite_sheet: sprite_sheet_handle,
|
texture_atlas: texture_atlas_handle,
|
||||||
sprite: SpriteSheetSprite {
|
sprite: TextureAtlasSprite {
|
||||||
index: 0,
|
index: 0,
|
||||||
scale: 6.0,
|
scale: 6.0,
|
||||||
position: Vec3::new(0.0, 0.0, 0.0),
|
position: Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
|
79
examples/2d/texture_atlas.rs
Normal file
79
examples/2d/texture_atlas.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_asset::{HandleId, LoadState};
|
||||||
|
use bevy_sprite::TextureAtlasBuilder;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::build()
|
||||||
|
.init_resource::<RpgSpriteHandles>()
|
||||||
|
.init_resource::<State>()
|
||||||
|
.add_default_plugins()
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.add_system(load_atlas.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct RpgSpriteHandles {
|
||||||
|
handles: Vec<HandleId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
command_buffer: &mut CommandBuffer,
|
||||||
|
mut rpg_sprite_handles: ResMut<RpgSpriteHandles>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
rpg_sprite_handles.handles = asset_server
|
||||||
|
.load_asset_folder("assets/textures/rpg")
|
||||||
|
.unwrap();
|
||||||
|
command_buffer
|
||||||
|
.build()
|
||||||
|
.add_entity(OrthographicCameraEntity::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct State {
|
||||||
|
atlas_loaded: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_atlas(
|
||||||
|
command_buffer: &mut CommandBuffer,
|
||||||
|
mut state: ResMut<State>,
|
||||||
|
rpg_sprite_handles: Res<RpgSpriteHandles>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
||||||
|
mut textures: ResMut<Assets<Texture>>,
|
||||||
|
) {
|
||||||
|
if state.atlas_loaded {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut texture_atlas_builder = TextureAtlasBuilder::new(Vec2::new(600., 600.));
|
||||||
|
if let Some(LoadState::Loaded(_)) =
|
||||||
|
asset_server.get_group_load_state(&rpg_sprite_handles.handles)
|
||||||
|
{
|
||||||
|
// TODO: sort by size (within atlas builder)
|
||||||
|
for texture_id in rpg_sprite_handles.handles.iter() {
|
||||||
|
let handle = Handle::from_id(*texture_id);
|
||||||
|
let texture = textures.get(&handle).unwrap();
|
||||||
|
texture_atlas_builder.add_texture(handle, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
let texture_atlas = texture_atlas_builder.finish(&mut textures);
|
||||||
|
let vendor_handle = asset_server
|
||||||
|
.get_handle("assets/textures/rpg/chars/vendor/generic-rpg-vendor.png")
|
||||||
|
.unwrap();
|
||||||
|
let vendor_index = texture_atlas.get_texture_index(vendor_handle).unwrap();
|
||||||
|
let atlas_handle = texture_atlases.add(texture_atlas);
|
||||||
|
command_buffer.build().add_entity(SpriteSheetEntity {
|
||||||
|
sprite: TextureAtlasSprite {
|
||||||
|
index: vendor_index as u32,
|
||||||
|
scale: 4.0,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
texture_atlas: atlas_handle,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
state.atlas_loaded = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,13 +24,13 @@ fn setup(
|
||||||
// ..Default::default()
|
// ..Default::default()
|
||||||
// });
|
// });
|
||||||
|
|
||||||
let texture = Texture::load(TextureType::Png(
|
let texture_handle = asset_server
|
||||||
"assets/branding/bevy_logo_dark_big.png".to_string(),
|
.load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
|
||||||
));
|
.unwrap();
|
||||||
|
|
||||||
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
|
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
|
||||||
|
let texture = textures.get(&texture_handle).unwrap();
|
||||||
let aspect = texture.aspect();
|
let aspect = texture.aspect();
|
||||||
let texture_handle = textures.add(texture);
|
|
||||||
|
|
||||||
let blue_material_handle = materials.add(Color::rgb(0.6, 0.6, 1.0).into());
|
let blue_material_handle = materials.add(Color::rgb(0.6, 0.6, 1.0).into());
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub use crate::{
|
||||||
scene::{Scene, SceneSpawner},
|
scene::{Scene, SceneSpawner},
|
||||||
sprite::{
|
sprite::{
|
||||||
entity::{SpriteEntity, SpriteSheetEntity},
|
entity::{SpriteEntity, SpriteSheetEntity},
|
||||||
ColorMaterial, Quad, Sprite, SpriteSheet, SpriteSheetSprite,
|
ColorMaterial, Quad, Sprite, TextureAtlas, TextureAtlasSprite,
|
||||||
},
|
},
|
||||||
text::Font,
|
text::Font,
|
||||||
transform::prelude::*,
|
transform::prelude::*,
|
||||||
|
|
Loading…
Reference in a new issue