Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture container support #306

Merged
merged 4 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ tiled = { version = "0.9", default-features = false }
[dev-dependencies.bevy]
version = "0.8"
default-features = false
features=["bevy_core_pipeline", "bevy_render", "bevy_asset", "png", "bevy_winit", "bevy_text", "bevy_sprite", "filesystem_watcher"]
features=["bevy_core_pipeline", "bevy_render", "bevy_asset", "png", "ktx2", "bevy_winit", "bevy_text", "bevy_sprite", "filesystem_watcher"]

[target.'cfg(unix)'.dev-dependencies.bevy]
version = "0.8"
default-features = false
features=["bevy_core_pipeline", "bevy_render", "bevy_asset", "png", "bevy_winit", "x11", "bevy_text", "bevy_sprite"]
features=["bevy_core_pipeline", "bevy_render", "bevy_asset", "png", "ktx2", "bevy_winit", "x11", "bevy_text", "bevy_sprite"]

[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"
Expand Down
Binary file added assets/hex-tiles.ktx2
Binary file not shown.
95 changes: 95 additions & 0 deletions examples/texture_container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use bevy::{prelude::*, render::texture::ImageSettings};
use bevy_ecs_tilemap::helpers::hex_grid::axial::AxialPos;
use bevy_ecs_tilemap::prelude::*;
use rand::prelude::SliceRandom;
use rand::thread_rng;
mod helpers;

const MAP_RADIUS: u32 = 10;
const MAP_DIAMETER: u32 = 2 * MAP_RADIUS + 1;
const MAP_CENTER: TilePos = TilePos {
x: MAP_RADIUS + 1,
y: MAP_RADIUS + 1,
};
const TILE_SIZE: TilemapTileSize = TilemapTileSize { x: 48.0, y: 54.0 };
const COORD_SYS: HexCoordSystem = HexCoordSystem::Row;

fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(Camera2dBundle::default());

// Most of the work is happening bevy side. In this case, using the `ktx2` feature. If this
// feature is not turned on, that the image won't properly be interpreted as a texture
// container. The other alternative is `dds`.
let texture_vec = TilemapTexture::TextureContainer(asset_server.load("hex-tiles.ktx2"));

let map_size = TilemapSize {
x: MAP_DIAMETER,
y: MAP_DIAMETER,
};

let mut tile_storage = TileStorage::empty(map_size);
let tilemap_entity = commands.spawn().id();
let tilemap_id = TilemapId(tilemap_entity);

let tile_positions = generate_hexagon(
AxialPos::from_tile_pos_given_coord_system(&MAP_CENTER, COORD_SYS),
MAP_RADIUS,
)
.into_iter()
.map(|axial_pos| axial_pos.as_tile_pos_given_coord_system(COORD_SYS));

let mut rng = thread_rng();
let weighted_tile_choices = [
(TileTexture(0), 0.8),
(TileTexture(1), 0.1),
(TileTexture(2), 0.1),
];
for position in tile_positions {
let texture = weighted_tile_choices
.choose_weighted(&mut rng, |choice| choice.1)
.unwrap()
.0;
let tile_entity = commands
.spawn()
.insert_bundle(TileBundle {
position,
tilemap_id,
texture,
..Default::default()
})
.id();
tile_storage.set(&position, tile_entity);
}

let tile_size = TILE_SIZE;
let grid_size = TILE_SIZE.into();

commands
.entity(tilemap_entity)
.insert_bundle(TilemapBundle {
grid_size,
tile_size,
size: map_size,
storage: tile_storage,
texture: texture_vec,
map_type: TilemapType::Hexagon(COORD_SYS),
transform: get_tilemap_center_transform(&map_size, &grid_size, 0.0),
..Default::default()
});
}

fn main() {
App::new()
.insert_resource(WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("Using TilemapTexture::TextureContainer"),
..Default::default()
})
.insert_resource(ImageSettings::default_linear())
.add_plugins(DefaultPlugins)
.add_plugin(TilemapPlugin)
.add_startup_system(startup)
.add_system(helpers::camera::movement)
.run();
}
2 changes: 1 addition & 1 deletion examples/texture_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn main() {
.insert_resource(WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("Using TilemapTexture::Vector instead of TilemapTexture::Single"),
title: String::from("Using TilemapTexture::Vector"),
..Default::default()
})
.insert_resource(ImageSettings::default_nearest())
Expand Down
13 changes: 13 additions & 0 deletions src/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ pub enum TilemapTexture {
/// available when `"atlas"` is not enabled.
#[cfg(not(feature = "atlas"))]
Vector(Vec<Handle<Image>>),
/// The tiles are provided as array layers inside a KTX2 or DDS container.
///
/// This only makes sense to use when the `"atlas"` feature is NOT enabled, as texture arrays
/// are required to handle storing an array of textures. Therefore, this variant is only
/// available when `"atlas"` is not enabled.
#[cfg(not(feature = "atlas"))]
TextureContainer(Handle<Image>),
}

impl Default for TilemapTexture {
Expand All @@ -110,6 +117,8 @@ impl TilemapTexture {
TilemapTexture::Single(handle) => vec![handle],
#[cfg(not(feature = "atlas"))]
TilemapTexture::Vector(handles) => handles.iter().collect(),
#[cfg(not(feature = "atlas"))]
TilemapTexture::TextureContainer(handle) => vec![handle],
}
}

Expand Down Expand Up @@ -156,6 +165,10 @@ impl TilemapTexture {
TilemapTexture::Vector(handles) => {
TilemapTexture::Vector(handles.iter().map(|h| h.clone_weak()).collect())
}
#[cfg(not(feature = "atlas"))]
TilemapTexture::TextureContainer(handle) => {
TilemapTexture::TextureContainer(handle.clone_weak())
}
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/render/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ impl ExtractedTilemapTexture {
}
(handles.len() as u32, tile_size.into())
}
#[cfg(not(feature = "atlas"))]
TilemapTexture::TextureContainer(image_handle) => {
let image = image_assets.get(image_handle).expect(
"Expected image to have finished loading if \
it is being extracted as a texture!",
);
let tile_size: TilemapTileSize = image.size().into();
(
image.texture_descriptor.array_layer_count(),
tile_size.into(),
)
}
};

ExtractedTilemapTexture {
Expand Down
4 changes: 3 additions & 1 deletion src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::marker::PhantomData;

use bevy::render::render_asset::RenderAssets;
use bevy::{
asset::load_internal_asset,
core_pipeline::core_2d::Transparent2d,
Expand Down Expand Up @@ -309,10 +310,11 @@ fn prepare_textures(
render_device: Res<RenderDevice>,
mut texture_array_cache: ResMut<TextureArrayCache>,
extracted_tilemap_textures: Query<&ExtractedTilemapTexture>,
render_images: Res<RenderAssets<Image>>,
) {
for extracted_texture in extracted_tilemap_textures.iter() {
texture_array_cache.add_extracted_texture(extracted_texture);
}

texture_array_cache.prepare(&render_device);
texture_array_cache.prepare(&render_device, &render_images);
}
132 changes: 81 additions & 51 deletions src/render/texture_array_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ impl TextureArrayCache {
}
(handles.len() as u32, tile_size.into())
}
TilemapTexture::TextureContainer(handle) => {
let image = image_assets.get(handle).expect(
"Expected image to have finished loading if \
it is being extracted as a texture!",
);
let tile_size: TilemapTileSize = image.size().into();
(
image.texture_descriptor.array_layer_count(),
tile_size.into(),
)
}
};

if !self.meta_data.contains_key(&texture) {
Expand All @@ -116,64 +127,80 @@ impl TextureArrayCache {
}

/// Prepares each texture array texture
pub fn prepare(&mut self, render_device: &RenderDevice) {
pub fn prepare(
&mut self,
render_device: &RenderDevice,
render_images: &Res<RenderAssets<Image>>,
) {
let prepare_queue = self.prepare_queue.drain().collect::<Vec<_>>();
for texture in prepare_queue {
let (count, tile_size, _, _, filter) = self.meta_data.get(&texture).unwrap();
for texture in prepare_queue.iter() {
match texture {
TilemapTexture::Single(_) | TilemapTexture::Vector(_) => {
let (count, tile_size, _, _, filter) = self.meta_data.get(texture).unwrap();

// Fixes weird cubemap bug.
let count = if *count == 6 { count + 1 } else { *count };
// Fixes weird cubemap bug.
let count = if *count == 6 { count + 1 } else { *count };

let gpu_texture = render_device.create_texture(&TextureDescriptor {
label: Some("texture_array"),
size: Extent3d {
width: tile_size.x as u32,
height: tile_size.y as u32,
depth_or_array_layers: count,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
});
let gpu_texture = render_device.create_texture(&TextureDescriptor {
label: Some("texture_array"),
size: Extent3d {
width: tile_size.x as u32,
height: tile_size.y as u32,
depth_or_array_layers: count,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
});

let sampler = render_device.create_sampler(&SamplerDescriptor {
label: Some("texture_array_sampler"),
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: *filter,
min_filter: *filter,
mipmap_filter: *filter,
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: None,
border_color: None,
});
let sampler = render_device.create_sampler(&SamplerDescriptor {
label: Some("texture_array_sampler"),
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: *filter,
min_filter: *filter,
mipmap_filter: *filter,
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: None,
border_color: None,
});

let texture_view = gpu_texture.create_view(&TextureViewDescriptor {
label: Some("texture_array_view"),
format: None,
dimension: Some(TextureViewDimension::D2Array),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: NonZeroU32::new(count),
});
let texture_view = gpu_texture.create_view(&TextureViewDescriptor {
label: Some("texture_array_view"),
format: None,
dimension: Some(TextureViewDimension::D2Array),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: NonZeroU32::new(count),
});

let gpu_image = GpuImage {
texture_format: TextureFormat::bevy_default(),
texture: gpu_texture,
sampler,
texture_view,
size: tile_size.into(),
};
let gpu_image = GpuImage {
texture_format: TextureFormat::bevy_default(),
texture: gpu_texture,
sampler,
texture_view,
size: tile_size.into(),
};

self.textures.insert(texture.clone_weak(), gpu_image);
self.queue_queue.insert(texture.clone_weak());
self.textures.insert(texture.clone_weak(), gpu_image);
self.queue_queue.insert(texture.clone_weak());
}
TilemapTexture::TextureContainer(handle) => {
if let Some(gpu_image) = render_images.get(handle) {
self.textures
.insert(texture.clone_weak(), gpu_image.clone());
} else {
self.prepare_queue.insert(texture.clone_weak());
}
}
}
}
}

Expand Down Expand Up @@ -294,6 +321,9 @@ impl TextureArrayCache {
let command_buffer = command_encoder.finish();
render_queue.submit(vec![command_buffer]);
}
TilemapTexture::TextureContainer(_) => {
// do nothing, we already have the necessary GPU image
}
}
}
}
Expand Down