2020-06-06 23:16:58 +00:00
|
|
|
use crate::{Rect, TextureAtlas};
|
2020-06-06 07:12:38 +00:00
|
|
|
use bevy_asset::{Assets, Handle};
|
2020-07-16 23:51:45 +00:00
|
|
|
use bevy_math::Vec2;
|
2020-07-26 19:08:41 +00:00
|
|
|
use bevy_render::texture::{Texture, TextureFormat};
|
2020-08-29 00:08:51 +00:00
|
|
|
use bevy_utils::HashMap;
|
2020-06-07 18:30:04 +00:00
|
|
|
use rectangle_pack::{
|
|
|
|
contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation,
|
|
|
|
RectToInsert, TargetBin,
|
|
|
|
};
|
|
|
|
use thiserror::Error;
|
2020-06-06 07:12:38 +00:00
|
|
|
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug)]
|
2020-06-06 07:12:38 +00:00
|
|
|
pub struct TextureAtlasBuilder {
|
2020-06-07 18:30:04 +00:00
|
|
|
pub textures: Vec<Handle<Texture>>,
|
|
|
|
pub rects_to_place: GroupedRectsToPlace<Handle<Texture>>,
|
|
|
|
pub initial_size: Vec2,
|
2020-06-06 23:16:58 +00:00
|
|
|
pub max_size: Vec2,
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TextureAtlasBuilder {
|
|
|
|
fn default() -> Self {
|
2020-06-06 23:16:58 +00:00
|
|
|
Self::new(Vec2::new(256., 256.), Vec2::new(2048., 2048.))
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 18:30:04 +00:00
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum RectanglePackError {
|
|
|
|
#[error("Could not pack textures into an atlas within the given bounds")]
|
|
|
|
NotEnoughSpace,
|
|
|
|
}
|
|
|
|
|
2020-06-06 07:12:38 +00:00
|
|
|
impl TextureAtlasBuilder {
|
2020-06-06 23:16:58 +00:00
|
|
|
pub fn new(initial_size: Vec2, max_size: Vec2) -> Self {
|
2020-06-06 07:12:38 +00:00
|
|
|
Self {
|
2020-06-07 18:30:04 +00:00
|
|
|
textures: Default::default(),
|
|
|
|
rects_to_place: GroupedRectsToPlace::new(),
|
|
|
|
initial_size,
|
2020-06-06 23:16:58 +00:00
|
|
|
max_size,
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 18:30:04 +00:00
|
|
|
pub fn add_texture(&mut self, texture_handle: Handle<Texture>, texture: &Texture) {
|
|
|
|
self.rects_to_place.push_rect(
|
|
|
|
texture_handle,
|
|
|
|
None,
|
2020-11-17 21:40:18 +00:00
|
|
|
RectToInsert::new(texture.size.x as u32, texture.size.y as u32, 1),
|
2020-06-07 18:30:04 +00:00
|
|
|
)
|
2020-06-06 23:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-06-07 18:30:04 +00:00
|
|
|
fn place_texture(
|
|
|
|
&mut self,
|
|
|
|
atlas_texture: &mut Texture,
|
|
|
|
texture: &Texture,
|
|
|
|
packed_location: &PackedLocation,
|
|
|
|
) {
|
|
|
|
let rect_width = packed_location.width() as usize;
|
|
|
|
let rect_height = packed_location.height() as usize;
|
|
|
|
let rect_x = packed_location.x() as usize;
|
|
|
|
let rect_y = packed_location.y() as usize;
|
2020-11-17 21:40:18 +00:00
|
|
|
let atlas_width = atlas_texture.size.x as usize;
|
2020-07-26 19:08:41 +00:00
|
|
|
let format_size = atlas_texture.format.pixel_size();
|
2020-06-07 18:30:04 +00:00
|
|
|
|
|
|
|
for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
|
2020-07-26 19:08:41 +00:00
|
|
|
let begin = (bound_y * atlas_width + rect_x) * 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;
|
2020-06-07 18:30:04 +00:00
|
|
|
atlas_texture.data[begin..end]
|
2020-06-06 07:12:38 +00:00
|
|
|
.copy_from_slice(&texture.data[texture_begin..texture_end]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 18:30:04 +00:00
|
|
|
pub fn finish(
|
|
|
|
mut self,
|
|
|
|
textures: &mut Assets<Texture>,
|
|
|
|
) -> Result<TextureAtlas, RectanglePackError> {
|
2020-11-17 21:40:18 +00:00
|
|
|
let initial_width = self.initial_size.x as u32;
|
|
|
|
let initial_height = self.initial_size.y as u32;
|
|
|
|
let max_width = self.max_size.x as u32;
|
|
|
|
let max_height = self.max_size.y as u32;
|
2020-06-07 18:30:04 +00:00
|
|
|
|
|
|
|
let mut current_width = initial_width;
|
|
|
|
let mut current_height = initial_height;
|
|
|
|
let mut rect_placements = None;
|
|
|
|
let mut atlas_texture = Texture::default();
|
|
|
|
|
|
|
|
while rect_placements.is_none() {
|
|
|
|
if current_width > max_width || current_height > max_height {
|
|
|
|
break;
|
|
|
|
}
|
2020-11-21 20:55:25 +00:00
|
|
|
|
|
|
|
let last_attempt = current_height == max_height && current_width == max_width;
|
|
|
|
|
2020-10-13 01:12:17 +00:00
|
|
|
let mut target_bins = std::collections::BTreeMap::new();
|
2020-06-07 18:30:04 +00:00
|
|
|
target_bins.insert(0, TargetBin::new(current_width, current_height, 1));
|
2020-11-21 20:55:25 +00:00
|
|
|
|
2020-06-07 18:30:04 +00:00
|
|
|
rect_placements = match pack_rects(
|
|
|
|
&self.rects_to_place,
|
|
|
|
target_bins,
|
|
|
|
&volume_heuristic,
|
|
|
|
&contains_smallest_box,
|
|
|
|
) {
|
2020-11-21 20:55:25 +00:00
|
|
|
Ok(rect_placements) => {
|
|
|
|
atlas_texture = Texture::new_fill(
|
|
|
|
Vec2::new(current_width as f32, current_height as f32),
|
|
|
|
&[0, 0, 0, 0],
|
|
|
|
TextureFormat::Rgba8UnormSrgb,
|
|
|
|
);
|
|
|
|
Some(rect_placements)
|
|
|
|
}
|
2020-06-07 18:30:04 +00:00
|
|
|
Err(rectangle_pack::RectanglePackError::NotEnoughBinSpace) => {
|
2020-11-21 20:55:25 +00:00
|
|
|
current_height = bevy_math::clamp(current_height * 2, 0, max_height);
|
|
|
|
current_width = bevy_math::clamp(current_width * 2, 0, max_width);
|
2020-06-07 18:30:04 +00:00
|
|
|
None
|
2020-06-15 19:47:35 +00:00
|
|
|
}
|
2020-11-21 20:55:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if last_attempt {
|
|
|
|
break;
|
2020-06-07 18:30:04 +00:00
|
|
|
}
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
2020-09-07 22:00:03 +00:00
|
|
|
let rect_placements = rect_placements.ok_or(RectanglePackError::NotEnoughSpace)?;
|
2020-06-07 18:30:04 +00:00
|
|
|
|
|
|
|
let mut texture_rects = Vec::with_capacity(rect_placements.packed_locations().len());
|
2020-08-29 00:08:51 +00:00
|
|
|
let mut texture_handles = HashMap::default();
|
2020-06-15 19:47:35 +00:00
|
|
|
for (texture_handle, (_, packed_location)) in rect_placements.packed_locations().iter() {
|
2020-06-07 18:30:04 +00:00
|
|
|
let texture = textures.get(texture_handle).unwrap();
|
|
|
|
let min = Vec2::new(packed_location.x() as f32, packed_location.y() as f32);
|
|
|
|
let max = min
|
|
|
|
+ Vec2::new(
|
|
|
|
packed_location.width() as f32,
|
|
|
|
packed_location.height() as f32,
|
|
|
|
);
|
2020-10-18 20:48:15 +00:00
|
|
|
texture_handles.insert(texture_handle.clone_weak(), texture_rects.len());
|
2020-06-07 18:30:04 +00:00
|
|
|
texture_rects.push(Rect { min, max });
|
|
|
|
self.place_texture(&mut atlas_texture, texture, packed_location);
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
2020-06-07 18:30:04 +00:00
|
|
|
Ok(TextureAtlas {
|
2020-06-14 01:53:31 +00:00
|
|
|
size: atlas_texture.size,
|
2020-06-07 18:30:04 +00:00
|
|
|
texture: textures.add(atlas_texture),
|
2020-06-06 07:12:38 +00:00
|
|
|
textures: texture_rects,
|
|
|
|
texture_handles: Some(texture_handles),
|
2020-06-07 18:30:04 +00:00
|
|
|
})
|
2020-06-06 07:12:38 +00:00
|
|
|
}
|
|
|
|
}
|