bevy_chair 0.13.0

Chair mesh asset loader plugin
Documentation
use crate::{
	atlas::*,
	vertex::*
};

use std::sync::Mutex;

use bevy_asset::*;
use bevy_render::{
	mesh::{Indices, Mesh},
	render_asset::RenderAssetUsages,
	render_resource::PrimitiveTopology
};
use bevy_utils::BoxedFuture;
use cached::proc_macro::once;
use chair::{MeshFeature, MeshReader};
use tokio::sync::oneshot::Receiver;

pub struct ChairLoader {
	atlas_rx: Mutex<Option<Receiver<ChairAtlas>>>,
	usages: RenderAssetUsages
}

impl ChairLoader {
	pub fn new(rx: Receiver<ChairAtlas>) -> Self {
		Self {
			atlas_rx: Mutex::new(Some(rx)),
			usages: RenderAssetUsages::RENDER_WORLD
		}
	}

	/// The usages for loaded meshes.
	/// By default it is only used in `RENDER_WORLD` to save memory.
	pub fn with_usages(self, usages: RenderAssetUsages) -> Self {
		Self {
			usages,
			..self
		}
	}
}

impl AssetLoader for ChairLoader {
	type Asset = Mesh;
	type Settings = ();
	type Error = chair::Error;

	fn load<'a>(
		&'a self,
		reader: &'a mut io::Reader,
		_settings: &'a Self::Settings,
		_load_context: &'a mut LoadContext
	) -> BoxedFuture<'a, chair::Result<Self::Asset>> {
		Box::pin(async move {
			let mut bytes = vec![];
			reader.read_to_end(&mut bytes).await
				.map_err(chair::Error::from)?;

			let atlas = get_atlas(&self.atlas_rx).await;
			let reader = MeshReader::new::<FatVertex>(atlas.0, Some("error"));
			let (features, vertices, indices) = reader.read::<FatVertex>(&bytes)?;

			// assemble the mesh struct depending on what features were enabled
			let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, self.usages);
			mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices.iter()
				.map(|v| v.pos)
				.collect::<Vec<_>>());
			if features.contains(MeshFeature::Coloured) {
				mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertices.iter()
					.map(|v| v.col)
					.collect::<Vec<_>>());
			}
			if features.contains(MeshFeature::Normals) {
				mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vertices.iter()
					.map(|v| v.normals)
					.collect::<Vec<_>>());
			}
			if features.contains(MeshFeature::Textured) {
				mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vertices.iter()
					.map(|v| v.uvs)
					.collect::<Vec<_>>());
			}

			mesh.insert_indices(Indices::U32(indices));

			Ok(mesh)
		})
	}

	fn extensions(&self) -> &[&str] {
		&["chr"]
	}
}

// cached in a way that it will only ever run once, and return the same reference each time
#[once(sync_writes = true)]
async fn get_atlas(rx: &Mutex<Option<Receiver<ChairAtlas>>>) -> ChairAtlas {
	let rx = {
		let mut lock = rx.lock().unwrap();
		lock.take()
			.expect("get_atlas called multiple times, bug in cached?")
	};

	rx.await
		.expect("Receiving atlas failed")
}