use std::{
any::TypeId,
marker::PhantomData,
sync::{Arc, RwLock},
};
use bevy::{
platform::collections::HashMap,
prelude::*,
reflect::{GetTypeRegistration, TypeInfo},
};
use thiserror::Error;
#[derive(Resource, Debug, Clone, Default)]
pub struct MaterialPropertyRegistry {
pub inner: Arc<RwLock<HashMap<String, TypeId>>>,
}
#[derive(Debug, Clone, Copy)]
pub struct MaterialProperty<T> {
pub key: &'static str,
_marker: PhantomData<T>,
}
impl<T> MaterialProperty<T> {
pub const fn new(key: &'static str) -> Self {
Self { key, _marker: PhantomData }
}
}
#[derive(Error, Debug, Clone)]
pub enum GetPropertyError {
#[error("Property not found")]
NotFound,
#[error("Property found doesn't have the required type. Type found: {:?}", found.map(TypeInfo::type_path))]
WrongType { found: Option<&'static TypeInfo> },
}
pub trait MaterialPropertyAppExt {
fn register_material_property_manual<T: Reflect + GetTypeRegistration>(&mut self, key: impl Into<String>) -> &mut Self;
fn register_material_property<T: Reflect + GetTypeRegistration>(&mut self, property: MaterialProperty<T>) -> &mut Self;
}
impl MaterialPropertyAppExt for App {
fn register_material_property_manual<T: Reflect + GetTypeRegistration>(&mut self, key: impl Into<String>) -> &mut Self {
let mut type_registry = self.world().resource::<AppTypeRegistry>().write();
if type_registry.get(TypeId::of::<T>()).is_none() {
type_registry.register::<T>();
}
drop(type_registry);
let mut property_map = self.world().resource::<MaterialPropertyRegistry>().inner.write().unwrap();
property_map.insert(key.into(), TypeId::of::<T>());
drop(property_map);
self
}
fn register_material_property<T: Reflect + GetTypeRegistration>(&mut self, property: MaterialProperty<T>) -> &mut Self {
self.register_material_property_manual::<T>(property.key)
}
}