Skip to content

Instantly share code, notes, and snippets.

@ethereumdegen
Created May 10, 2024 19:43
Show Gist options
  • Save ethereumdegen/0b2c99677e710d32ae494dd7e735e6f0 to your computer and use it in GitHub Desktop.
Save ethereumdegen/0b2c99677e710d32ae494dd7e735e6f0 to your computer and use it in GitHub Desktop.
use crate::file_system_interaction::asset_loading::TextureAssets;
use bevy::{prelude::*, utils::HashMap};
use bevy_asset_loader::mapped::AssetFileName;
pub struct TextureAtlasCombined {
pub layout: Handle<TextureAtlasLayout>,
pub image: Handle<Image>,
}
#[derive(Resource, Default)]
pub(crate) struct TextureAtlasAssets {
pub(crate) gui_pixel_icons_atlas: Option<TextureAtlasCombined>,
pub(crate) ability_icons_atlas: Option<TextureAtlasCombined>,
pub(crate) item_icons_atlas: Option<TextureAtlasCombined>,
// pub(crate) particles_atlas: Option<Handle<TextureAtlas>>,
}
impl TextureAtlasAssets {
pub fn build (
mut commands: Commands,
texture_assets: Res<TextureAssets>,
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
mut images: ResMut<Assets<Image>>,
) {
// Assume defaults for padding and sampling, adjust as needed
let padding = Some(UVec2::new(2, 2));
//let sampling = Some(ImageSampler::linear());
let gui_pixel_icons_atlas = build_texture_atlas(
&texture_assets.gui_pixel_icons,
Some(Vec2::new(2048., 2048.)),
padding,
&mut texture_atlases,
&mut images,
);
let ability_icons_atlas = build_texture_atlas(
&texture_assets.ability_icons,
Some(Vec2::new(2048., 2048.)),
padding,
&mut texture_atlases,
&mut images,
);
let item_icons_atlas = build_texture_atlas(
&texture_assets.item_icons,
Some(Vec2::new(2048., 2048.)),
padding,
&mut texture_atlases,
&mut images,
);
// Store the handles in new resource
commands.insert_resource(TextureAtlasAssets {
gui_pixel_icons_atlas: Some(gui_pixel_icons_atlas),
ability_icons_atlas: Some(ability_icons_atlas),
item_icons_atlas: Some(item_icons_atlas),
});
}
}
pub fn get_index_for_subtexture_by_name(
texture_atlas_handle: &Handle<TextureAtlasLayout>,
texture_atlases: &Res<Assets<TextureAtlasLayout>>,
image_handles_map: &HashMap<AssetFileName, Handle<Image>>,
texture_name: &String,
) -> Option<usize> {
if let Some(atlas) = texture_atlases.get(texture_atlas_handle) {
if let Some(image_handle) = image_handles_map.get(texture_name.as_str()) {
return atlas.get_texture_index(image_handle);
}
}
//self.index_registry.get(texture_name) .copied() //why do we need to do plus 1 ?
None
}
pub fn build_texture_atlas(
handles: &HashMap<AssetFileName, Handle<Image>>,
max_size: Option<Vec2>,
padding: Option<UVec2>,
// textures: &mut ResMut<Assets<Image>>,
texture_atlases: &mut ResMut<Assets<TextureAtlasLayout>>,
images: &mut ResMut<Assets<Image>>,
) -> TextureAtlasCombined {
let mut texture_atlas_builder = TextureAtlasBuilder::default()
.max_size(max_size.unwrap_or(Vec2::new(2048., 2048.)))
.padding(padding.unwrap_or(UVec2::ZERO));
// let mut texture_atlas_index_registry: TextureAtlasIndexRegistry = HashMap::new();
for (icon_name, handle) in handles.iter() {
if let Some(texture) = images.get(handle) {
texture_atlas_builder.add_texture(Some(handle.clone_weak().into()), texture);
// texture_atlas_index_registry.insert(icon_name.clone(), index) ;
// println!("register atlas image {:?} {:?}", icon_name,index);
} else {
panic!(
"Texture handle did not resolve to an `Image` asset: {:?}",
icon_name
);
// continue;
}
}
let (texture_atlas, image) = texture_atlas_builder
.finish()
.expect("Failed to build texture atlas.");
let texture_atlas_handle = texture_atlases.add(texture_atlas);
let image_handle = images.add(image);
TextureAtlasCombined {
layout: texture_atlas_handle,
image: image_handle,
}
}
use bevy::ecs::system::EntityCommands;
use bevy::ecs::system::EntityCommand;
use bevy::prelude::*;
use crate::{
file_system_interaction::{
ability_type::AbilityType,
asset_loading::{AbilitySystemTypeAssets, ItemSystemTypeAssets, TextureAssets},
item_type::ItemType,
texture_atlas::{get_index_for_subtexture_by_name, TextureAtlasAssets},
},
ui::{element_linker::LinkedUiElement, inventory_menu::InventoryUiEquipmentSlot},
};
pub(crate) fn ui_icons_plugin(app: &mut App) {
app.add_systems(
Update,
update_icons_from_source.run_if(any_with_component::<UiIconComponent>),
);
}
#[derive(Component, Default)]
pub struct UiIconComponent {
pub icon_source: Option<UiIconSource>,
}
impl UiIconComponent {
pub fn new (icon_source: Option<UiIconSource> ) -> Self {
Self {
icon_source
}
}
}
struct SetIconSource(Option<UiIconSource>);
impl EntityCommand for SetIconSource {
fn apply(self, entity: Entity, world: &mut World) {
if let Some(mut ui_icon_comp) = world.entity_mut(entity).get_mut::<UiIconComponent>() {
ui_icon_comp.icon_source = self.0 ;
}
}
}
pub trait UiIconComponentCommands<'a> {
fn set_icon_source(&'a mut self, src: Option<UiIconSource> ) -> &mut EntityCommands<'a>;
}
impl<'a> UiIconComponentCommands<'a> for EntityCommands<'a> {
fn set_icon_source(&'a mut self, src: Option<UiIconSource>) -> &mut EntityCommands<'a> {
self.add(SetIconSource(src))
}
}
pub enum UiIconSource {
AbilityType(String),
ItemType(String),
GuiPixelIcon(String)
}
/*
rebuild this in a more generic way with traits and having access to World? trait returns string and needs resources from World to compute ? ..?
*/
fn update_icons_from_source(
mut image_node_query: Query<
(&mut Visibility, &mut TextureAtlas, &mut UiImage, &UiIconComponent),
Changed<UiIconComponent>,
>,
ability_system_type_assets: Res<AbilitySystemTypeAssets>,
item_system_type_assets: Res<ItemSystemTypeAssets>,
item_types: Res<Assets<ItemType>>,
ability_types: Res<Assets<AbilityType>>,
texture_atlases: Res<Assets<TextureAtlasLayout>>,
texture_atlas_assets: Res<TextureAtlasAssets>,
images: Res<TextureAssets>,
) {
//render equipped stuff like weapons that are on you !!
for (mut _visibility, mut tex_atlas, mut ui_image, ui_icon_comp) in image_node_query.iter_mut() {
let Some(icon_source) = &ui_icon_comp.icon_source else {
let icons_atlas = &texture_atlas_assets.gui_pixel_icons_atlas.as_ref().unwrap();
let icons_handles_map = &images.gui_pixel_icons;
//set to blank icon
let Some(image_index) = get_index_for_subtexture_by_name(
&icons_atlas.layout,
&texture_atlases,
&icons_handles_map,
&"icon_transparent.png".to_string(),
) else {
warn!("could not set icon source (None) to icon_transparent");
continue;
};
tex_atlas.layout = icons_atlas.layout.clone();
tex_atlas.index = image_index;
ui_image.texture = icons_atlas.image.clone();
continue;
};
let icon_name = match &icon_source {
UiIconSource::ItemType(item_type_name) => {
if let Some(item_type_handle) = item_system_type_assets
.item_types
.get(item_type_name.as_str())
{
if let Some(item_type) = item_types.get(item_type_handle) {
if let Some(equipment_item_icon_name) = &item_type.icon_texture {
Some(equipment_item_icon_name.clone())
} else {
None
}
} else {
None
}
} else {
None
}
}
UiIconSource::AbilityType(ability_type_name) => {
if let Some(ability_type_handle) = ability_system_type_assets
.ability_types
.get(ability_type_name.as_str())
{
if let Some(ability_type) = ability_types.get(ability_type_handle) {
if let Some(ability_icon_name) = &ability_type.icon_texture {
Some(ability_icon_name.clone())
} else {
None
}
} else {
None
}
} else {
None
}
},
UiIconSource::GuiPixelIcon(icon_name) => Some(icon_name.clone())
};
let Some(icon_name) = icon_name else { continue };
let icons_handles_map = match &icon_source {
UiIconSource::ItemType(_) => Some(&images.item_icons),
UiIconSource::AbilityType(_) => Some(&images.ability_icons),
UiIconSource::GuiPixelIcon(_) => Some(&images.gui_pixel_icons),
};
let Some(icons_handles_map) = icons_handles_map else {
continue;
};
let texture_atlas = match &icon_source {
UiIconSource::ItemType(_) => texture_atlas_assets.item_icons_atlas.as_ref().unwrap() ,
UiIconSource::AbilityType(_) => texture_atlas_assets.ability_icons_atlas.as_ref().unwrap() ,
UiIconSource::GuiPixelIcon(_) => texture_atlas_assets.gui_pixel_icons_atlas.as_ref().unwrap(),
};
let Some(image_index) = get_index_for_subtexture_by_name(
&texture_atlas.layout,
&texture_atlases,
&icons_handles_map,
&icon_name,
) else {
continue;
};
ui_image.texture = texture_atlas.image.clone();
tex_atlas.layout = texture_atlas.layout.clone();
tex_atlas.index = image_index;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment