Texture atlas format and conversion (#1365)
* can specify texture format for a texture atlas * add automatic conversion
This commit is contained in:
parent
cf5f3b5008
commit
6b8f8a7ed0
@ -16,8 +16,6 @@ impl AssetLoader for ImageTextureLoader {
|
|||||||
load_context: &'a mut LoadContext,
|
load_context: &'a mut LoadContext,
|
||||||
) -> BoxedFuture<'a, Result<()>> {
|
) -> BoxedFuture<'a, Result<()>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
use bevy_core::AsBytes;
|
|
||||||
|
|
||||||
// Find the image type we expect. A file with the extension "png" should
|
// Find the image type we expect. A file with the extension "png" should
|
||||||
// probably load as a PNG.
|
// probably load as a PNG.
|
||||||
|
|
||||||
@ -41,118 +39,7 @@ impl AssetLoader for ImageTextureLoader {
|
|||||||
|
|
||||||
let dyn_img = image::load_from_memory_with_format(bytes, img_format)?;
|
let dyn_img = image::load_from_memory_with_format(bytes, img_format)?;
|
||||||
|
|
||||||
let width;
|
load_context.set_default_asset(LoadedAsset::new(image_to_texture(dyn_img)));
|
||||||
let height;
|
|
||||||
|
|
||||||
let data: Vec<u8>;
|
|
||||||
let format: TextureFormat;
|
|
||||||
|
|
||||||
match dyn_img {
|
|
||||||
image::DynamicImage::ImageLuma8(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::R8Unorm;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageLumaA8(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rg8Unorm;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageRgb8(i) => {
|
|
||||||
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageRgba8(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageBgr8(i) => {
|
|
||||||
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
|
|
||||||
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Bgra8UnormSrgb;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageBgra8(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Bgra8UnormSrgb;
|
|
||||||
|
|
||||||
data = i.into_raw();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageLuma16(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::R16Uint;
|
|
||||||
|
|
||||||
let raw_data = i.into_raw();
|
|
||||||
|
|
||||||
data = raw_data.as_slice().as_bytes().to_owned();
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageLumaA16(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rg16Uint;
|
|
||||||
|
|
||||||
let raw_data = i.into_raw();
|
|
||||||
|
|
||||||
data = raw_data.as_slice().as_bytes().to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
image::DynamicImage::ImageRgb16(image) => {
|
|
||||||
width = image.width();
|
|
||||||
height = image.height();
|
|
||||||
format = TextureFormat::Rgba16Uint;
|
|
||||||
|
|
||||||
let mut local_data =
|
|
||||||
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
|
|
||||||
|
|
||||||
for pixel in image.into_raw().chunks_exact(3) {
|
|
||||||
// TODO unsafe_get in release builds?
|
|
||||||
let r = pixel[0];
|
|
||||||
let g = pixel[1];
|
|
||||||
let b = pixel[2];
|
|
||||||
let a = u16::max_value();
|
|
||||||
|
|
||||||
local_data.extend_from_slice(&r.to_ne_bytes());
|
|
||||||
local_data.extend_from_slice(&g.to_ne_bytes());
|
|
||||||
local_data.extend_from_slice(&b.to_ne_bytes());
|
|
||||||
local_data.extend_from_slice(&a.to_ne_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
data = local_data;
|
|
||||||
}
|
|
||||||
image::DynamicImage::ImageRgba16(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rgba16Uint;
|
|
||||||
|
|
||||||
let raw_data = i.into_raw();
|
|
||||||
|
|
||||||
data = raw_data.as_slice().as_bytes().to_owned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let texture = Texture::new(
|
|
||||||
Extent3d::new(width, height, 1),
|
|
||||||
TextureDimension::D2,
|
|
||||||
data,
|
|
||||||
format,
|
|
||||||
);
|
|
||||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -173,3 +60,152 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to convert a `DynamicImage` to a `Texture`
|
||||||
|
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
|
||||||
|
use bevy_core::AsBytes;
|
||||||
|
|
||||||
|
let width;
|
||||||
|
let height;
|
||||||
|
|
||||||
|
let data: Vec<u8>;
|
||||||
|
let format: TextureFormat;
|
||||||
|
|
||||||
|
match dyn_img {
|
||||||
|
image::DynamicImage::ImageLuma8(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::R8Unorm;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageLumaA8(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rg8Unorm;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageRgb8(i) => {
|
||||||
|
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageRgba8(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageBgr8(i) => {
|
||||||
|
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
|
||||||
|
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Bgra8UnormSrgb;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageBgra8(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Bgra8UnormSrgb;
|
||||||
|
|
||||||
|
data = i.into_raw();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageLuma16(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::R16Uint;
|
||||||
|
|
||||||
|
let raw_data = i.into_raw();
|
||||||
|
|
||||||
|
data = raw_data.as_slice().as_bytes().to_owned();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageLumaA16(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rg16Uint;
|
||||||
|
|
||||||
|
let raw_data = i.into_raw();
|
||||||
|
|
||||||
|
data = raw_data.as_slice().as_bytes().to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
image::DynamicImage::ImageRgb16(image) => {
|
||||||
|
width = image.width();
|
||||||
|
height = image.height();
|
||||||
|
format = TextureFormat::Rgba16Uint;
|
||||||
|
|
||||||
|
let mut local_data =
|
||||||
|
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
|
||||||
|
|
||||||
|
for pixel in image.into_raw().chunks_exact(3) {
|
||||||
|
// TODO unsafe_get in release builds?
|
||||||
|
let r = pixel[0];
|
||||||
|
let g = pixel[1];
|
||||||
|
let b = pixel[2];
|
||||||
|
let a = u16::max_value();
|
||||||
|
|
||||||
|
local_data.extend_from_slice(&r.to_ne_bytes());
|
||||||
|
local_data.extend_from_slice(&g.to_ne_bytes());
|
||||||
|
local_data.extend_from_slice(&b.to_ne_bytes());
|
||||||
|
local_data.extend_from_slice(&a.to_ne_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
data = local_data;
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageRgba16(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rgba16Uint;
|
||||||
|
|
||||||
|
let raw_data = i.into_raw();
|
||||||
|
|
||||||
|
data = raw_data.as_slice().as_bytes().to_owned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::new(
|
||||||
|
Extent3d::new(width, height, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
|
data,
|
||||||
|
format,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are
|
||||||
|
/// covered, it will return `None` if the format is not supported
|
||||||
|
pub(crate) fn texture_to_image(texture: &Texture) -> Option<image::DynamicImage> {
|
||||||
|
match texture.format {
|
||||||
|
TextureFormat::R8Unorm => image::ImageBuffer::from_raw(
|
||||||
|
texture.size.width,
|
||||||
|
texture.size.height,
|
||||||
|
texture.data.clone(),
|
||||||
|
)
|
||||||
|
.map(image::DynamicImage::ImageLuma8),
|
||||||
|
TextureFormat::Rg8Unorm => image::ImageBuffer::from_raw(
|
||||||
|
texture.size.width,
|
||||||
|
texture.size.height,
|
||||||
|
texture.data.clone(),
|
||||||
|
)
|
||||||
|
.map(image::DynamicImage::ImageLumaA8),
|
||||||
|
TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw(
|
||||||
|
texture.size.width,
|
||||||
|
texture.size.height,
|
||||||
|
texture.data.clone(),
|
||||||
|
)
|
||||||
|
.map(image::DynamicImage::ImageRgba8),
|
||||||
|
TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw(
|
||||||
|
texture.size.width,
|
||||||
|
texture.size.height,
|
||||||
|
texture.data.clone(),
|
||||||
|
)
|
||||||
|
.map(image::DynamicImage::ImageBgra8),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -125,6 +125,37 @@ impl Texture {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a texture from a format to another
|
||||||
|
/// Only a few formats are supported as input and output:
|
||||||
|
/// - `TextureFormat::R8Unorm`
|
||||||
|
/// - `TextureFormat::Rg8Unorm`
|
||||||
|
/// - `TextureFormat::Rgba8UnormSrgb`
|
||||||
|
/// - `TextureFormat::Bgra8UnormSrgb`
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "png",
|
||||||
|
feature = "dds",
|
||||||
|
feature = "tga",
|
||||||
|
feature = "jpeg",
|
||||||
|
feature = "bmp"
|
||||||
|
))]
|
||||||
|
pub fn convert(&self, new_format: TextureFormat) -> Option<Self> {
|
||||||
|
super::texture_to_image(self)
|
||||||
|
.and_then(|img| match new_format {
|
||||||
|
TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())),
|
||||||
|
TextureFormat::Rg8Unorm => {
|
||||||
|
Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8()))
|
||||||
|
}
|
||||||
|
TextureFormat::Rgba8UnormSrgb => {
|
||||||
|
Some(image::DynamicImage::ImageRgba8(img.into_rgba8()))
|
||||||
|
}
|
||||||
|
TextureFormat::Bgra8UnormSrgb => {
|
||||||
|
Some(image::DynamicImage::ImageBgra8(img.into_bgra8()))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.map(super::image_to_texture)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn texture_resource_system(
|
pub fn texture_resource_system(
|
||||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||||
textures: Res<Assets<Texture>>,
|
textures: Res<Assets<Texture>>,
|
||||||
|
@ -18,6 +18,7 @@ bevy_app = { path = "../bevy_app", version = "0.4.0" }
|
|||||||
bevy_asset = { path = "../bevy_asset", version = "0.4.0" }
|
bevy_asset = { path = "../bevy_asset", version = "0.4.0" }
|
||||||
bevy_core = { path = "../bevy_core", version = "0.4.0" }
|
bevy_core = { path = "../bevy_core", version = "0.4.0" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
|
||||||
|
bevy_log = { path = "../bevy_log", version = "0.4.0" }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.4.0" }
|
bevy_math = { path = "../bevy_math", version = "0.4.0" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
|
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
|
||||||
bevy_render = { path = "../bevy_render", version = "0.4.0" }
|
bevy_render = { path = "../bevy_render", version = "0.4.0" }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{Rect, TextureAtlas};
|
use crate::{Rect, TextureAtlas};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
|
use bevy_log::{debug, error, warn};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
|
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
@ -13,6 +14,8 @@ use thiserror::Error;
|
|||||||
pub enum TextureAtlasBuilderError {
|
pub enum TextureAtlasBuilderError {
|
||||||
#[error("could not pack textures into an atlas within the given bounds")]
|
#[error("could not pack textures into an atlas within the given bounds")]
|
||||||
NotEnoughSpace,
|
NotEnoughSpace,
|
||||||
|
#[error("added a texture with the wrong format in an atlas")]
|
||||||
|
WrongFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -26,6 +29,10 @@ pub struct TextureAtlasBuilder {
|
|||||||
initial_size: Vec2,
|
initial_size: Vec2,
|
||||||
/// The absolute maximum size of the texture atlas in pixels.
|
/// The absolute maximum size of the texture atlas in pixels.
|
||||||
max_size: Vec2,
|
max_size: Vec2,
|
||||||
|
/// The texture format for the textures that will be loaded in the atlas.
|
||||||
|
format: TextureFormat,
|
||||||
|
/// Enable automatic format conversion for textures if they are not in the atlas format.
|
||||||
|
auto_format_conversion: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextureAtlasBuilder {
|
impl Default for TextureAtlasBuilder {
|
||||||
@ -34,6 +41,8 @@ impl Default for TextureAtlasBuilder {
|
|||||||
rects_to_place: GroupedRectsToPlace::new(),
|
rects_to_place: GroupedRectsToPlace::new(),
|
||||||
initial_size: Vec2::new(256., 256.),
|
initial_size: Vec2::new(256., 256.),
|
||||||
max_size: Vec2::new(2048., 2048.),
|
max_size: Vec2::new(2048., 2048.),
|
||||||
|
format: TextureFormat::Rgba8UnormSrgb,
|
||||||
|
auto_format_conversion: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,6 +62,18 @@ impl TextureAtlasBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the texture format for textures in the atlas.
|
||||||
|
pub fn format(mut self, format: TextureFormat) -> Self {
|
||||||
|
self.format = format;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Control whether the added texture should be converted to the atlas format, if different.
|
||||||
|
pub fn auto_format_conversion(mut self, auto_format_conversion: bool) -> Self {
|
||||||
|
self.auto_format_conversion = auto_format_conversion;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a texture to be copied to the texture atlas.
|
/// Adds a texture to be copied to the texture atlas.
|
||||||
pub fn add_texture(&mut self, texture_handle: Handle<Texture>, texture: &Texture) {
|
pub fn add_texture(&mut self, texture_handle: Handle<Texture>, texture: &Texture) {
|
||||||
self.rects_to_place.push_rect(
|
self.rects_to_place.push_rect(
|
||||||
@ -62,8 +83,7 @@ impl TextureAtlasBuilder {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_texture(
|
fn copy_texture_to_atlas(
|
||||||
&mut self,
|
|
||||||
atlas_texture: &mut Texture,
|
atlas_texture: &mut Texture,
|
||||||
texture: &Texture,
|
texture: &Texture,
|
||||||
packed_location: &PackedLocation,
|
packed_location: &PackedLocation,
|
||||||
@ -85,6 +105,28 @@ impl TextureAtlasBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn copy_converted_texture(
|
||||||
|
&self,
|
||||||
|
atlas_texture: &mut Texture,
|
||||||
|
texture: &Texture,
|
||||||
|
packed_location: &PackedLocation,
|
||||||
|
) {
|
||||||
|
if self.format == texture.format {
|
||||||
|
Self::copy_texture_to_atlas(atlas_texture, texture, packed_location);
|
||||||
|
} else if let Some(converted_texture) = texture.convert(self.format) {
|
||||||
|
debug!(
|
||||||
|
"Converting texture from '{:?}' to '{:?}'",
|
||||||
|
texture.format, self.format
|
||||||
|
);
|
||||||
|
Self::copy_texture_to_atlas(atlas_texture, &converted_texture, packed_location);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Error converting texture from '{:?}' to '{:?}', ignoring",
|
||||||
|
texture.format, self.format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the builder and returns a result with a new texture atlas.
|
/// Consumes the builder and returns a result with a new texture atlas.
|
||||||
///
|
///
|
||||||
/// Internally it copies all rectangles from the textures and copies them
|
/// Internally it copies all rectangles from the textures and copies them
|
||||||
@ -97,7 +139,7 @@ impl TextureAtlasBuilder {
|
|||||||
/// If there is not enough space in the atlas texture, an error will
|
/// If there is not enough space in the atlas texture, an error will
|
||||||
/// be returned. It is then recommended to make a larger sprite sheet.
|
/// be returned. It is then recommended to make a larger sprite sheet.
|
||||||
pub fn finish(
|
pub fn finish(
|
||||||
mut self,
|
self,
|
||||||
textures: &mut Assets<Texture>,
|
textures: &mut Assets<Texture>,
|
||||||
) -> Result<TextureAtlas, TextureAtlasBuilderError> {
|
) -> Result<TextureAtlas, TextureAtlasBuilderError> {
|
||||||
let initial_width = self.initial_size.x as u32;
|
let initial_width = self.initial_size.x as u32;
|
||||||
@ -130,7 +172,7 @@ impl TextureAtlasBuilder {
|
|||||||
Extent3d::new(current_width, current_height, 1),
|
Extent3d::new(current_width, current_height, 1),
|
||||||
TextureDimension::D2,
|
TextureDimension::D2,
|
||||||
&[0, 0, 0, 0],
|
&[0, 0, 0, 0],
|
||||||
TextureFormat::Rgba8UnormSrgb,
|
self.format,
|
||||||
);
|
);
|
||||||
Some(rect_placements)
|
Some(rect_placements)
|
||||||
}
|
}
|
||||||
@ -160,7 +202,14 @@ impl TextureAtlasBuilder {
|
|||||||
);
|
);
|
||||||
texture_handles.insert(texture_handle.clone_weak(), texture_rects.len());
|
texture_handles.insert(texture_handle.clone_weak(), texture_rects.len());
|
||||||
texture_rects.push(Rect { min, max });
|
texture_rects.push(Rect { min, max });
|
||||||
self.copy_texture(&mut atlas_texture, texture, packed_location);
|
if texture.format != self.format && !self.auto_format_conversion {
|
||||||
|
warn!(
|
||||||
|
"Loading a texture of format '{:?}' in an atlas with format '{:?}'",
|
||||||
|
texture.format, self.format
|
||||||
|
);
|
||||||
|
return Err(TextureAtlasBuilderError::WrongFormat);
|
||||||
|
}
|
||||||
|
self.copy_converted_texture(&mut atlas_texture, texture, packed_location);
|
||||||
}
|
}
|
||||||
Ok(TextureAtlas {
|
Ok(TextureAtlas {
|
||||||
size: atlas_texture.size.as_vec3().truncate(),
|
size: atlas_texture.size.as_vec3().truncate(),
|
||||||
|
Loading…
Reference in New Issue
Block a user