bevy_chair 0.13.0

Chair mesh asset loader plugin
Documentation
#![allow(clippy::tabs_in_doc_comments)]

mod atlas;
mod config;
mod loader;
mod vertex;

pub use crate::{
	atlas::*,
	config::*,
	loader::*
};

use bevy_asset::*;
use	bevy_app::*;
use bevy_ecs::prelude::*;
use bevy_render::prelude::Image;
use bevy_sprite::TextureAtlasLayout;
use log::*;
use tokio::sync::oneshot::*;

/// The chair loading plugin
///
/// # Usage
/// To use, add it before the asset plugin:
/// ```rust
/// # use bevy::prelude::*;
/// use bevy_chair::*;
/// # fn main() {
/// App::new()
/// 	.add_plugin(AssetPlugin::default())
/// 	.add_plugin(ChairPlugin::default());
/// # }
/// ```
///
/// Then you can load meshes!
/// ```
/// # use bevy::prelude::*;
/// # use bevy_chair::*;
/// # fn main() { App::new().add_plugin(CorePlugin::default()).add_plugin(AssetPlugin::default()).add_plugin(ChairPlugin).add_startup_system(load_chair).run(); }
/// fn load_chair(assets: Res<AssetServer>) {
/// 	let mesh: Handle<Mesh> = assets.load("chair.chr");
/// 	println!("Loaded mesh handle {mesh:?}");
/// }
/// ```
///
/// Keep in mind you must use `ChairAtlas::texture()` handle in your materials since atlas uvs are baked into the mesh.
#[derive(Default)]
pub struct ChairPlugin;

impl Plugin for ChairPlugin {
	fn build(&self, app: &mut App) {
		// apply default config if none given
		if !app.world.contains_resource::<ChairConfig>() {
			app.world.init_resource::<ChairConfig>();
		}

		let (atlas_tx, atlas_rx) = channel();
		app.register_asset_loader(ChairLoader::new(atlas_rx))
			.init_state::<ChairState>()
			.insert_resource(AtlasSender(atlas_tx))
			.add_systems(OnEnter(ChairState::Setup), create_atlas)
			.add_systems(Update, check_ready.run_if(in_state(ChairState::Setup)))
			.add_systems(OnEnter(ChairState::Load), load_atlas)
			.add_systems(OnEnter(ChairState::Packed), send_atlas);
	}
}

#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, States)]
pub enum ChairState {
	#[default]
	Setup,
	Load,
	Packed,
	Ready
}

#[derive(Resource)]
struct AtlasSender(Sender<ChairAtlas>);

fn create_atlas(
	mut commands: Commands,
	assets: Res<AssetServer>,
	config: Res<ChairConfig>
) {
	let atlas = RawChairAtlas::new(&assets, config.textures);
	debug!("Created atlas");
	commands.insert_resource(MutChairAtlas::new(atlas));
}

fn check_ready(
	mut state: ResMut<NextState<ChairState>>,
	atlas: Res<MutChairAtlas>,
	assets: Res<AssetServer>,
	folders: Res<Assets<LoadedFolder>>
) {
	if assets.load_state(atlas.0.folder.id()) != LoadState::Loaded {
		return;
	}

	let Some(folder) = folders.get(&atlas.0.folder) else {
		error!("Folder handle does not exist?!");
		return;
	};

	for handle in &folder.handles {
		if assets.load_state(handle.id()) != LoadState::Loaded {
			return;
		}
	}

	info!("Atlas textures loaded");
	state.set(ChairState::Load);
}

fn load_atlas(
	mut atlas_mut: ResMut<MutChairAtlas>,
	mut folders: ResMut<Assets<LoadedFolder>>,
	mut textures: ResMut<Assets<Image>>,
	mut atlases: ResMut<Assets<TextureAtlasLayout>>,
	mut state: ResMut<NextState<ChairState>>
) {
	atlas_mut.0.load(&mut folders, &mut textures, &mut atlases);
	state.set(ChairState::Packed);
	info!("Packed atlas");
}

fn send_atlas(world: &mut World) {
	let atlas_mut = world.remove_resource::<MutChairAtlas>().unwrap();
	let atlas = ChairAtlas(atlas_mut.0);
	world.insert_resource(atlas.clone());

	let sender = world.remove_resource::<AtlasSender>().unwrap();
	sender.0.send(atlas)
		.expect("ChairLoader died");
	let mut state = world.resource_mut::<NextState<ChairState>>();
	state.set(ChairState::Ready);
	debug!("Sent atlas");
}