mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
Extend the Texture asset type to support 3D data (#903)
Extend the Texture asset type to support 3D data Textures are still loaded from images as 2D, but they can be reshaped according to how the render pipeline would like to use them. Also add an example of how this can be used with the texture2DArray uniform type.
This commit is contained in:
parent
eb587b2f10
commit
46fac78774
17 changed files with 279 additions and 51 deletions
|
@ -266,6 +266,10 @@ path = "examples/shader/mesh_custom_attribute.rs"
|
||||||
name = "shader_custom_material"
|
name = "shader_custom_material"
|
||||||
path = "examples/shader/shader_custom_material.rs"
|
path = "examples/shader/shader_custom_material.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "array_texture"
|
||||||
|
path = "examples/shader/array_texture.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "shader_defs"
|
name = "shader_defs"
|
||||||
path = "examples/shader/shader_defs.rs"
|
path = "examples/shader/shader_defs.rs"
|
||||||
|
|
BIN
assets/textures/array_texture.png
Normal file
BIN
assets/textures/array_texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 460 KiB |
|
@ -11,7 +11,9 @@ use bevy_render::{
|
||||||
pipeline::PrimitiveTopology,
|
pipeline::PrimitiveTopology,
|
||||||
prelude::{Color, Texture},
|
prelude::{Color, Texture},
|
||||||
render_graph::base,
|
render_graph::base,
|
||||||
texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat},
|
texture::{
|
||||||
|
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDimension, TextureFormat,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use bevy_scene::Scene;
|
use bevy_scene::Scene;
|
||||||
use bevy_transform::{
|
use bevy_transform::{
|
||||||
|
@ -136,7 +138,8 @@ async fn load_gltf<'a, 'b>(
|
||||||
&texture_label,
|
&texture_label,
|
||||||
LoadedAsset::new(Texture {
|
LoadedAsset::new(Texture {
|
||||||
data: image.clone().into_vec(),
|
data: image.clone().into_vec(),
|
||||||
size: bevy_math::f32::vec2(size.0 as f32, size.1 as f32),
|
size: Extent3d::new(size.0, size.1, 1),
|
||||||
|
dimension: TextureDimension::D2,
|
||||||
format: TextureFormat::Rgba8Unorm,
|
format: TextureFormat::Rgba8Unorm,
|
||||||
sampler: texture_sampler(&texture)?,
|
sampler: texture_sampler(&texture)?,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -34,13 +34,17 @@ impl Node for TextureCopyNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
let texture_descriptor: TextureDescriptor = texture.into();
|
let texture_descriptor: TextureDescriptor = texture.into();
|
||||||
let width = texture.size.x as usize;
|
let width = texture.size.width as usize;
|
||||||
let aligned_width = render_context
|
let aligned_width =
|
||||||
.resources()
|
render_context.resources().get_aligned_texture_size(width);
|
||||||
.get_aligned_texture_size(texture.size.x as usize);
|
|
||||||
let format_size = texture.format.pixel_size();
|
let format_size = texture.format.pixel_size();
|
||||||
let mut aligned_data =
|
let mut aligned_data = vec![
|
||||||
vec![0; format_size * aligned_width * texture.size.y as usize];
|
0;
|
||||||
|
format_size
|
||||||
|
* aligned_width
|
||||||
|
* texture.size.height as usize
|
||||||
|
* texture.size.depth as usize
|
||||||
|
];
|
||||||
texture
|
texture
|
||||||
.data
|
.data
|
||||||
.chunks_exact(format_size * width)
|
.chunks_exact(format_size * width)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use super::{Texture, TextureFormat};
|
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||||
use bevy_math::Vec2;
|
|
||||||
use bevy_utils::BoxedFuture;
|
use bevy_utils::BoxedFuture;
|
||||||
|
|
||||||
/// Loads HDR textures as Texture assets
|
/// Loads HDR textures as Texture assets
|
||||||
|
@ -37,7 +36,8 @@ impl AssetLoader for HdrTextureLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
let texture = Texture::new(
|
let texture = Texture::new(
|
||||||
Vec2::new(info.width as f32, info.height as f32),
|
Extent3d::new(info.width, info.height, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
rgba_data,
|
rgba_data,
|
||||||
format,
|
format,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use super::{Texture, TextureFormat};
|
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||||
use bevy_math::Vec2;
|
|
||||||
use bevy_utils::BoxedFuture;
|
use bevy_utils::BoxedFuture;
|
||||||
|
|
||||||
/// Loader for images that can be read by the `image` crate.
|
/// Loader for images that can be read by the `image` crate.
|
||||||
|
@ -148,7 +147,12 @@ impl AssetLoader for ImageTextureLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let texture = Texture::new(Vec2::new(width as f32, height as f32), data, format);
|
let texture = Texture::new(
|
||||||
|
Extent3d::new(width, height, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
|
data,
|
||||||
|
format,
|
||||||
|
);
|
||||||
load_context.set_default_asset(LoadedAsset::new(texture));
|
load_context.set_default_asset(LoadedAsset::new(texture));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use super::{SamplerDescriptor, TextureDescriptor, TextureFormat};
|
use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat};
|
||||||
use crate::renderer::{
|
use crate::renderer::{
|
||||||
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
|
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
|
||||||
};
|
};
|
||||||
use bevy_app::prelude::{EventReader, Events};
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
use bevy_asset::{AssetEvent, Assets, Handle};
|
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||||
use bevy_ecs::{Res, ResMut};
|
use bevy_ecs::{Res, ResMut};
|
||||||
use bevy_math::Vec2;
|
|
||||||
use bevy_type_registry::TypeUuid;
|
use bevy_type_registry::TypeUuid;
|
||||||
use bevy_utils::HashSet;
|
use bevy_utils::HashSet;
|
||||||
|
|
||||||
|
@ -16,8 +15,9 @@ pub const SAMPLER_ASSET_INDEX: u64 = 1;
|
||||||
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
|
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
pub size: Vec2,
|
pub size: Extent3d,
|
||||||
pub format: TextureFormat,
|
pub format: TextureFormat,
|
||||||
|
pub dimension: TextureDimension,
|
||||||
pub sampler: SamplerDescriptor,
|
pub sampler: SamplerDescriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,31 +25,48 @@ impl Default for Texture {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Texture {
|
Texture {
|
||||||
data: Default::default(),
|
data: Default::default(),
|
||||||
size: Default::default(),
|
size: Extent3d {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
depth: 1,
|
||||||
|
},
|
||||||
format: TextureFormat::Rgba8UnormSrgb,
|
format: TextureFormat::Rgba8UnormSrgb,
|
||||||
|
dimension: TextureDimension::D2,
|
||||||
sampler: Default::default(),
|
sampler: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texture {
|
impl Texture {
|
||||||
pub fn new(size: Vec2, data: Vec<u8>, format: TextureFormat) -> Self {
|
pub fn new(
|
||||||
|
size: Extent3d,
|
||||||
|
dimension: TextureDimension,
|
||||||
|
data: Vec<u8>,
|
||||||
|
format: TextureFormat,
|
||||||
|
) -> Self {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
size.x as usize * size.y as usize * format.pixel_size(),
|
size.volume() * format.pixel_size(),
|
||||||
data.len(),
|
data.len(),
|
||||||
"Pixel data, size and format have to match",
|
"Pixel data, size and format have to match",
|
||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
size,
|
size,
|
||||||
|
dimension,
|
||||||
format,
|
format,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_fill(size: Vec2, pixel: &[u8], format: TextureFormat) -> Self {
|
pub fn new_fill(
|
||||||
|
size: Extent3d,
|
||||||
|
dimension: TextureDimension,
|
||||||
|
pixel: &[u8],
|
||||||
|
format: TextureFormat,
|
||||||
|
) -> Self {
|
||||||
let mut value = Texture {
|
let mut value = Texture {
|
||||||
format,
|
format,
|
||||||
|
dimension,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
value.resize(size);
|
value.resize(size);
|
||||||
|
@ -70,16 +87,42 @@ impl Texture {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aspect(&self) -> f32 {
|
pub fn aspect_2d(&self) -> f32 {
|
||||||
self.size.y / self.size.x
|
self.size.height as f32 / self.size.width as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, size: Vec2) {
|
pub fn resize(&mut self, size: Extent3d) {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
let width = size.x as usize;
|
|
||||||
let height = size.y as usize;
|
|
||||||
self.data
|
self.data
|
||||||
.resize(width * height * self.format.pixel_size(), 0);
|
.resize(size.volume() * self.format.pixel_size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the same.
|
||||||
|
pub fn reinterpret_size(&mut self, new_size: Extent3d) {
|
||||||
|
assert!(
|
||||||
|
new_size.volume() == self.size.volume(),
|
||||||
|
"Incompatible sizes: old = {:?} new = {:?}",
|
||||||
|
self.size,
|
||||||
|
new_size
|
||||||
|
);
|
||||||
|
|
||||||
|
self.size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets it as a 2D array texture,
|
||||||
|
/// where each of the stacked images becomes one layer of the array. This is primarily for use with the `texture2DArray`
|
||||||
|
/// shader uniform type.
|
||||||
|
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
|
||||||
|
// Must be a stacked image, and the height must be divisible by layers.
|
||||||
|
assert!(self.dimension == TextureDimension::D2);
|
||||||
|
assert!(self.size.depth == 1);
|
||||||
|
assert_eq!(self.size.height % layers, 0);
|
||||||
|
|
||||||
|
self.reinterpret_size(Extent3d {
|
||||||
|
width: self.size.width,
|
||||||
|
height: self.size.height / layers,
|
||||||
|
depth: layers,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture_resource_system(
|
pub fn texture_resource_system(
|
||||||
|
|
|
@ -14,14 +14,10 @@ pub struct TextureDescriptor {
|
||||||
impl From<&Texture> for TextureDescriptor {
|
impl From<&Texture> for TextureDescriptor {
|
||||||
fn from(texture: &Texture) -> Self {
|
fn from(texture: &Texture) -> Self {
|
||||||
TextureDescriptor {
|
TextureDescriptor {
|
||||||
size: Extent3d {
|
size: texture.size,
|
||||||
width: texture.size.x as u32,
|
|
||||||
height: texture.size.y as u32,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
mip_level_count: 1,
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: TextureDimension::D2,
|
dimension: texture.dimension,
|
||||||
format: texture.format,
|
format: texture.format,
|
||||||
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
|
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// NOTE: These are currently just copies of the wgpu types, but they might change in the future
|
// NOTE: These are currently just copies of the wgpu types, but they might change in the future
|
||||||
|
|
||||||
|
use bevy_math::Vec3;
|
||||||
|
|
||||||
/// Dimensions of a particular texture view.
|
/// Dimensions of a particular texture view.
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum TextureViewDimension {
|
pub enum TextureViewDimension {
|
||||||
|
@ -27,6 +29,24 @@ pub struct Extent3d {
|
||||||
pub depth: u32,
|
pub depth: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Extent3d {
|
||||||
|
pub fn new(width: u32, height: u32, depth: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn volume(&self) -> usize {
|
||||||
|
(self.width * self.height * self.depth) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vec3(&self) -> Vec3 {
|
||||||
|
Vec3::new(self.width as f32, self.height as f32, self.depth as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Type of data shaders will read from a texture.
|
/// Type of data shaders will read from a texture.
|
||||||
#[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum TextureComponentType {
|
pub enum TextureComponentType {
|
||||||
|
|
|
@ -24,8 +24,8 @@ impl DynamicTextureAtlasBuilder {
|
||||||
texture: &Texture,
|
texture: &Texture,
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
let allocation = self.atlas_allocator.allocate(size2(
|
let allocation = self.atlas_allocator.allocate(size2(
|
||||||
texture.size.x as i32 + self.padding,
|
texture.size.width as i32 + self.padding,
|
||||||
texture.size.y as i32 + self.padding,
|
texture.size.height as i32 + self.padding,
|
||||||
));
|
));
|
||||||
if let Some(allocation) = allocation {
|
if let Some(allocation) = allocation {
|
||||||
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
|
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
|
||||||
|
@ -72,7 +72,7 @@ impl DynamicTextureAtlasBuilder {
|
||||||
let mut rect = allocation.rectangle;
|
let mut rect = allocation.rectangle;
|
||||||
rect.max.x -= self.padding;
|
rect.max.x -= self.padding;
|
||||||
rect.max.y -= self.padding;
|
rect.max.y -= self.padding;
|
||||||
let atlas_width = atlas_texture.size.x as usize;
|
let atlas_width = atlas_texture.size.width as usize;
|
||||||
let rect_width = rect.width() as usize;
|
let rect_width = rect.width() as usize;
|
||||||
let format_size = atlas_texture.format.pixel_size();
|
let format_size = atlas_texture.format.pixel_size();
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub fn sprite_system(
|
||||||
let material = materials.get(handle).unwrap();
|
let material = materials.get(handle).unwrap();
|
||||||
if let Some(ref texture_handle) = material.texture {
|
if let Some(ref texture_handle) = material.texture {
|
||||||
if let Some(texture) = textures.get(texture_handle) {
|
if let Some(texture) = textures.get(texture_handle) {
|
||||||
sprite.size = texture.size;
|
sprite.size = texture.size.as_vec3().truncate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{Rect, TextureAtlas};
|
use crate::{Rect, TextureAtlas};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_render::texture::{Texture, TextureFormat};
|
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use rectangle_pack::{
|
use rectangle_pack::{
|
||||||
contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation,
|
contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation,
|
||||||
|
@ -43,7 +43,7 @@ impl TextureAtlasBuilder {
|
||||||
self.rects_to_place.push_rect(
|
self.rects_to_place.push_rect(
|
||||||
texture_handle,
|
texture_handle,
|
||||||
None,
|
None,
|
||||||
RectToInsert::new(texture.size.x as u32, texture.size.y as u32, 1),
|
RectToInsert::new(texture.size.width, texture.size.height, 1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ impl TextureAtlasBuilder {
|
||||||
let rect_height = packed_location.height() as usize;
|
let rect_height = packed_location.height() as usize;
|
||||||
let rect_x = packed_location.x() as usize;
|
let rect_x = packed_location.x() as usize;
|
||||||
let rect_y = packed_location.y() as usize;
|
let rect_y = packed_location.y() as usize;
|
||||||
let atlas_width = atlas_texture.size.x as usize;
|
let atlas_width = atlas_texture.size.width as usize;
|
||||||
let format_size = atlas_texture.format.pixel_size();
|
let format_size = atlas_texture.format.pixel_size();
|
||||||
|
|
||||||
for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
|
for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
|
||||||
|
@ -93,7 +93,6 @@ impl TextureAtlasBuilder {
|
||||||
|
|
||||||
let mut target_bins = std::collections::BTreeMap::new();
|
let mut target_bins = std::collections::BTreeMap::new();
|
||||||
target_bins.insert(0, TargetBin::new(current_width, current_height, 1));
|
target_bins.insert(0, TargetBin::new(current_width, current_height, 1));
|
||||||
|
|
||||||
rect_placements = match pack_rects(
|
rect_placements = match pack_rects(
|
||||||
&self.rects_to_place,
|
&self.rects_to_place,
|
||||||
target_bins,
|
target_bins,
|
||||||
|
@ -102,7 +101,8 @@ impl TextureAtlasBuilder {
|
||||||
) {
|
) {
|
||||||
Ok(rect_placements) => {
|
Ok(rect_placements) => {
|
||||||
atlas_texture = Texture::new_fill(
|
atlas_texture = Texture::new_fill(
|
||||||
Vec2::new(current_width as f32, current_height as f32),
|
Extent3d::new(current_width, current_height, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
&[0, 0, 0, 0],
|
&[0, 0, 0, 0],
|
||||||
TextureFormat::Rgba8UnormSrgb,
|
TextureFormat::Rgba8UnormSrgb,
|
||||||
);
|
);
|
||||||
|
@ -137,7 +137,7 @@ impl TextureAtlasBuilder {
|
||||||
self.place_texture(&mut atlas_texture, texture, packed_location);
|
self.place_texture(&mut atlas_texture, texture, packed_location);
|
||||||
}
|
}
|
||||||
Ok(TextureAtlas {
|
Ok(TextureAtlas {
|
||||||
size: atlas_texture.size,
|
size: atlas_texture.size.as_vec3().truncate(),
|
||||||
texture: textures.add(atlas_texture),
|
texture: textures.add(atlas_texture),
|
||||||
textures: texture_rects,
|
textures: texture_rects,
|
||||||
texture_handles: Some(texture_handles),
|
texture_handles: Some(texture_handles),
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph};
|
use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph};
|
||||||
use bevy_math::Vec2;
|
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
color::Color,
|
color::Color,
|
||||||
texture::{Texture, TextureFormat},
|
texture::{Extent3d, Texture, TextureDimension, TextureFormat},
|
||||||
};
|
};
|
||||||
use bevy_type_registry::TypeUuid;
|
use bevy_type_registry::TypeUuid;
|
||||||
|
|
||||||
|
@ -36,7 +35,8 @@ impl Font {
|
||||||
(color.b() * 255.0) as u8,
|
(color.b() * 255.0) as u8,
|
||||||
];
|
];
|
||||||
Texture::new(
|
Texture::new(
|
||||||
Vec2::new(width as f32, height as f32),
|
Extent3d::new(width as u32, height as u32, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
alpha
|
alpha
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| {
|
.map(|a| {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ab_glyph::GlyphId;
|
use ab_glyph::GlyphId;
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_render::texture::{Texture, TextureFormat};
|
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
|
||||||
use bevy_sprite::{DynamicTextureAtlasBuilder, TextureAtlas};
|
use bevy_sprite::{DynamicTextureAtlasBuilder, TextureAtlas};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ impl FontAtlas {
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
) -> FontAtlas {
|
) -> FontAtlas {
|
||||||
let atlas_texture = textures.add(Texture::new_fill(
|
let atlas_texture = textures.add(Texture::new_fill(
|
||||||
size,
|
Extent3d::new(size.x as u32, size.y as u32, 1),
|
||||||
|
TextureDimension::D2,
|
||||||
&[0, 0, 0, 0],
|
&[0, 0, 0, 0],
|
||||||
TextureFormat::Rgba8UnormSrgb,
|
TextureFormat::Rgba8UnormSrgb,
|
||||||
));
|
));
|
||||||
|
|
|
@ -28,8 +28,8 @@ pub fn image_node_system(
|
||||||
.and_then(|texture_handle| textures.get(texture_handle))
|
.and_then(|texture_handle| textures.get(texture_handle))
|
||||||
{
|
{
|
||||||
calculated_size.size = Size {
|
calculated_size.size = Size {
|
||||||
width: texture.size.x,
|
width: texture.size.width as f32,
|
||||||
height: texture.size.y,
|
height: texture.size.height as f32,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl WgpuRenderResourceContext {
|
||||||
layout: wgpu::TextureDataLayout {
|
layout: wgpu::TextureDataLayout {
|
||||||
offset: source_offset,
|
offset: source_offset,
|
||||||
bytes_per_row: source_bytes_per_row,
|
bytes_per_row: source_bytes_per_row,
|
||||||
rows_per_image: 0, // NOTE: Example sets this to 0, but should it be height?
|
rows_per_image: size.height,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wgpu::TextureCopyView {
|
wgpu::TextureCopyView {
|
||||||
|
|
153
examples/shader/array_texture.rs
Normal file
153
examples/shader/array_texture.rs
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
mesh::shape,
|
||||||
|
pipeline::{PipelineDescriptor, RenderPipeline},
|
||||||
|
render_graph::{base, AssetRenderResourcesNode, RenderGraph},
|
||||||
|
renderer::RenderResources,
|
||||||
|
shader::{ShaderStage, ShaderStages},
|
||||||
|
},
|
||||||
|
type_registry::TypeUuid,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This example illustrates how to create a texture for use with a texture2DArray shader uniform variable.
|
||||||
|
fn main() {
|
||||||
|
App::build()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_asset::<MyArrayTexture>()
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.add_system(create_array_texture.system())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RenderResources, Default, TypeUuid)]
|
||||||
|
#[uuid = "93fb26fc-6c05-489b-9029-601edf703b6b"]
|
||||||
|
struct MyArrayTexture {
|
||||||
|
pub texture: Handle<Texture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const VERTEX_SHADER: &str = r#"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 Vertex_Position;
|
||||||
|
layout(location = 0) out vec4 v_Position;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform Camera {
|
||||||
|
mat4 ViewProj;
|
||||||
|
};
|
||||||
|
layout(set = 1, binding = 0) uniform Transform {
|
||||||
|
mat4 Model;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
|
||||||
|
gl_Position = v_Position;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
const FRAGMENT_SHADER: &str = r#"
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 v_Position;
|
||||||
|
layout(location = 0) out vec4 o_Target;
|
||||||
|
|
||||||
|
layout(set = 1, binding = 1) uniform texture2DArray MyArrayTexture_texture;
|
||||||
|
layout(set = 1, binding = 2) uniform sampler MyArrayTexture_texture_sampler;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Screen-space coordinates determine which layer of the array texture we sample.
|
||||||
|
vec2 ss = v_Position.xy / v_Position.w;
|
||||||
|
float layer = 0.0;
|
||||||
|
if (ss.x > 0.0 && ss.y > 0.0) {
|
||||||
|
layer = 0.0;
|
||||||
|
} else if (ss.x < 0.0 && ss.y > 0.0) {
|
||||||
|
layer = 1.0;
|
||||||
|
} else if (ss.x > 0.0 && ss.y < 0.0) {
|
||||||
|
layer = 2.0;
|
||||||
|
} else {
|
||||||
|
layer = 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to texture coordinates.
|
||||||
|
vec2 uv = (ss + vec2(1.0)) / 2.0;
|
||||||
|
|
||||||
|
o_Target = texture(sampler2DArray(MyArrayTexture_texture, MyArrayTexture_texture_sampler), vec3(uv, layer));
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
struct LoadingTexture(Option<Handle<Texture>>);
|
||||||
|
|
||||||
|
struct MyPipeline(Handle<PipelineDescriptor>);
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
commands: &mut Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
|
||||||
|
mut shaders: ResMut<Assets<Shader>>,
|
||||||
|
mut render_graph: ResMut<RenderGraph>,
|
||||||
|
) {
|
||||||
|
// Start loading the texture.
|
||||||
|
commands.insert_resource(LoadingTexture(Some(
|
||||||
|
asset_server.load("textures/array_texture.png"),
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Create a new shader pipeline.
|
||||||
|
let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
|
||||||
|
vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
|
||||||
|
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
|
||||||
|
}));
|
||||||
|
commands.insert_resource(MyPipeline(pipeline_handle));
|
||||||
|
|
||||||
|
// Add an AssetRenderResourcesNode to our Render Graph. This will bind MyArrayTexture resources to our shader.
|
||||||
|
render_graph.add_system_node(
|
||||||
|
"my_array_texture",
|
||||||
|
AssetRenderResourcesNode::<MyArrayTexture>::new(true),
|
||||||
|
);
|
||||||
|
// Add a Render Graph edge connecting our new "my_array_texture" node to the main pass node. This ensures "my_array_texture"
|
||||||
|
// runs before the main pass.
|
||||||
|
render_graph
|
||||||
|
.add_node_edge("my_array_texture", base::node::MAIN_PASS)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
commands.spawn(Camera3dBundle {
|
||||||
|
transform: Transform::from_translation(Vec3::new(2.0, 2.0, 2.0))
|
||||||
|
.looking_at(Vec3::default(), Vec3::unit_y()),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_array_texture(
|
||||||
|
commands: &mut Commands,
|
||||||
|
my_pipeline: Res<MyPipeline>,
|
||||||
|
mut loading_texture: ResMut<LoadingTexture>,
|
||||||
|
mut textures: ResMut<Assets<Texture>>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut array_textures: ResMut<Assets<MyArrayTexture>>,
|
||||||
|
) {
|
||||||
|
let (handle, texture) = match loading_texture.0.as_ref() {
|
||||||
|
Some(handle) => {
|
||||||
|
if let Some(texture) = textures.get_mut(handle) {
|
||||||
|
(loading_texture.0.take().unwrap(), texture)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a new array texture asset from the loaded texture.
|
||||||
|
let array_layers = 4;
|
||||||
|
texture.reinterpret_stacked_2d_as_array(array_layers);
|
||||||
|
let array_texture = array_textures.add(MyArrayTexture { texture: handle });
|
||||||
|
|
||||||
|
// Spawn a cube that's shaded using the array texture.
|
||||||
|
commands
|
||||||
|
.spawn(MeshBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
|
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
|
||||||
|
my_pipeline.0.clone(),
|
||||||
|
)]),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.with(array_texture);
|
||||||
|
}
|
Loading…
Reference in a new issue