This commit is contained in:
Emerson Coskey 2024-11-09 22:23:27 -08:00
parent 8d90624061
commit 430447c943
No known key found for this signature in database
6 changed files with 120 additions and 50 deletions

View file

@ -2,6 +2,8 @@ pub mod component;
pub mod material; pub mod material;
pub mod material_data; pub mod material_data;
pub mod material_pipeline; pub mod material_pipeline;
pub mod shaders;
pub mod specialize;
pub mod prelude { pub mod prelude {
pub use super::material::{Material, MaterialPlugin}; pub use super::material::{Material, MaterialPlugin};
@ -21,14 +23,4 @@ mod tests {
pub struct TestPipeline; pub struct TestPipeline;
type TestMaterial<M> = MaterialComponent<M, TestPipeline>; type TestMaterial<M> = MaterialComponent<M, TestPipeline>;
impl MaterialPipeline for TestPipeline {
type MaterialProperties = ();
type ShaderKey = ();
type PipelineContext<'a, M: Material<Self>> = ();
fn material_plugin<M: Material<Self>>() -> impl Plugin {
|_: &mut App| {}
}
}
} }

View file

@ -3,24 +3,26 @@ use bevy_ecs::{
system::{lifetimeless::SRes, SystemParamItem}, system::{lifetimeless::SRes, SystemParamItem},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
use bevy_utils::HashMap;
use core::marker::PhantomData; use core::marker::PhantomData;
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::{Asset, AssetApp, AssetId, AssetPath, AssetServer, Handle}; use bevy_asset::{Asset, AssetApp, AssetId, AssetServer};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{Query, ResMut, Resource}; use bevy_ecs::system::{Query, ResMut, Resource};
use bevy_render::{ use bevy_render::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin}, render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin},
render_resource::{AsBindGroup, AsBindGroupError, BindGroupLayout, PreparedBindGroup, Shader}, render_resource::{AsBindGroup, AsBindGroupError, BindGroupLayout, PreparedBindGroup},
renderer::RenderDevice, renderer::RenderDevice,
sync_world::{MainEntity, MainEntityHashMap}, sync_world::{MainEntity, MainEntityHashMap},
view::ViewVisibility, view::ViewVisibility,
Extract, ExtractSchedule, RenderApp, Extract, ExtractSchedule, RenderApp,
}; };
use crate::component::MaterialComponent;
use crate::material_pipeline::MaterialPipeline; use crate::material_pipeline::MaterialPipeline;
use crate::{
component::MaterialComponent,
shaders::{LoadedShaders, Shaders},
};
pub enum SpecializeMaterialError {} pub enum SpecializeMaterialError {}
@ -34,24 +36,6 @@ pub trait Material<P: MaterialPipeline>: BaseMaterial {
fn specialize(ctx: P::PipelineContext<'_, Self>) -> Result<(), SpecializeMaterialError>; fn specialize(ctx: P::PipelineContext<'_, Self>) -> Result<(), SpecializeMaterialError>;
} }
#[derive(Deref)]
pub struct Shaders<P: MaterialPipeline> {
shaders: HashMap<P::ShaderKey, AssetPath<'static>>,
}
impl<P: MaterialPipeline, A: Into<AssetPath<'static>>> FromIterator<(P::ShaderKey, A)>
for Shaders<P>
{
fn from_iter<T: IntoIterator<Item = (P::ShaderKey, A)>>(iter: T) -> Self {
Self {
shaders: iter
.into_iter()
.map(|(key, path)| (key, path.into()))
.collect(),
}
}
}
pub struct MaterialPlugin<M: Material<P>, P: MaterialPipeline>(PhantomData<fn(M, P)>); pub struct MaterialPlugin<M: Material<P>, P: MaterialPipeline>(PhantomData<fn(M, P)>);
impl<M: Material<P>, P: MaterialPipeline> Default for MaterialPlugin<M, P> { impl<M: Material<P>, P: MaterialPipeline> Default for MaterialPlugin<M, P> {
@ -146,7 +130,7 @@ impl<M: BaseMaterial> RenderAsset for MaterialBindGroup<M> {
pub struct MaterialProperties<M: Material<R>, R: MaterialPipeline> { pub struct MaterialProperties<M: Material<R>, R: MaterialPipeline> {
#[deref] #[deref]
properties: R::MaterialProperties, properties: R::MaterialProperties,
_data: PhantomData<M>, _data: PhantomData<fn(M)>,
} }
impl<M: Material<R>, R: MaterialPipeline> MaterialProperties<M, R> { impl<M: Material<R>, R: MaterialPipeline> MaterialProperties<M, R> {
@ -191,19 +175,18 @@ impl<M: BaseMaterial> FromWorld for MaterialLayout<M> {
#[derive(Deref, Resource)] #[derive(Deref, Resource)]
pub struct MaterialShaders<M: Material<P>, P: MaterialPipeline> { pub struct MaterialShaders<M: Material<P>, P: MaterialPipeline> {
#[deref] #[deref]
shaders: HashMap<P::ShaderKey, Handle<Shader>>, shaders: LoadedShaders<P>,
_data: PhantomData<fn(M)>, _data: PhantomData<fn(M)>,
} }
impl<M: Material<P>, P: MaterialPipeline> FromWorld for MaterialShaders<M, P> { impl<M: Material<P>, P: MaterialPipeline> FromWorld for MaterialShaders<M, P> {
fn from_world(world: &mut World) -> Self { fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>(); let asset_server = world.resource::<AssetServer>();
let mut shaders = P::default_shaders();
shaders.extend(M::shaders());
Self { Self {
shaders: M::shaders() shaders: shaders.load(asset_server),
.shaders
.into_iter()
.map(|(key, path)| (key, asset_server.load(path)))
.collect(),
_data: PhantomData, _data: PhantomData,
} }
} }

View file

@ -23,14 +23,10 @@ pub struct MaterialData<'w, M: Material<P>, P: MaterialPipeline> {
} }
impl<'w, M: Material<P>, P: MaterialPipeline> MaterialData<'w, M, P> { impl<'w, M: Material<P>, P: MaterialPipeline> MaterialData<'w, M, P> {
pub fn get( pub fn get(&self, main_entity: MainEntity, id: AssetId<M>) -> Option<MaterialInstance<M, P>> {
&self,
main_entity: MainEntity,
id: AssetId<M>,
) -> Option<PreparedMaterialInstance<M, P>> {
let bind_group = self.bind_groups.get(id)?; let bind_group = self.bind_groups.get(id)?;
let properties = self.properties.get(id)?; let properties = self.properties.get(id)?;
Some(PreparedMaterialInstance { Some(MaterialInstance {
main_entity, main_entity,
layout: &self.layout, layout: &self.layout,
shaders: &self.shaders, shaders: &self.shaders,
@ -42,14 +38,14 @@ impl<'w, M: Material<P>, P: MaterialPipeline> MaterialData<'w, M, P> {
pub fn iter<'a>( pub fn iter<'a>(
&'a self, &'a self,
instances: &'a MaterialInstances<M, P>, instances: &'a MaterialInstances<M, P>,
) -> impl Iterator<Item = PreparedMaterialInstance<'a, M, P>> + 'a { ) -> impl Iterator<Item = MaterialInstance<'a, M, P>> {
instances instances
.iter() .iter()
.filter_map(|(main_entity, material_id)| self.get(*main_entity, *material_id)) .filter_map(|(main_entity, material_id)| self.get(*main_entity, *material_id))
} }
} }
pub struct PreparedMaterialInstance<'a, M: Material<P>, P: MaterialPipeline> { pub struct MaterialInstance<'a, M: Material<P>, P: MaterialPipeline> {
pub main_entity: MainEntity, pub main_entity: MainEntity,
pub properties: &'a P::MaterialProperties, pub properties: &'a P::MaterialProperties,
pub shaders: &'a MaterialShaders<M, P>, pub shaders: &'a MaterialShaders<M, P>,

View file

@ -1,13 +1,16 @@
use bevy_app::Plugin; use bevy_app::Plugin;
use bevy_ecs::system::{ReadOnlySystemParam, SystemParamItem};
use bevy_reflect::TypePath; use bevy_reflect::TypePath;
use core::hash::Hash; use core::hash::Hash;
use crate::material::Material; use crate::{material::Material, shaders::Shaders};
pub trait MaterialPipeline: TypePath + Sized + 'static { pub trait MaterialPipeline: TypePath + Sized + 'static {
type MaterialProperties: Send + Sync + 'static; type MaterialProperties: Send + Sync;
type ShaderKey: Hash + Eq + Send + Sync + 'static; type ShaderKey: Hash + Eq + Send + Sync;
type PipelineContext<'a, M: Material<Self>>; type PipelineContext<'a, M: Material<Self>>;
fn default_shaders() -> Shaders<Self>;
fn material_plugin<M: Material<Self>>() -> impl Plugin; fn material_plugin<M: Material<Self>>() -> impl Plugin;
} }

View file

@ -0,0 +1,75 @@
use bevy_asset::{AssetPath, AssetServer, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_render::render_resource::Shader;
use bevy_utils::HashMap;
use crate::material_pipeline::MaterialPipeline;
#[derive(Deref, DerefMut, Clone)]
pub struct Shaders<P: MaterialPipeline> {
shaders: HashMap<P::ShaderKey, AssetPath<'static>>,
}
impl<P: MaterialPipeline> Default for Shaders<P> {
fn default() -> Self {
Self {
shaders: Default::default(),
}
}
}
impl<P: MaterialPipeline, A: Into<AssetPath<'static>>> FromIterator<(P::ShaderKey, A)>
for Shaders<P>
{
fn from_iter<T: IntoIterator<Item = (P::ShaderKey, A)>>(iter: T) -> Self {
Self {
shaders: iter
.into_iter()
.map(|(key, path)| (key, path.into()))
.collect(),
}
}
}
impl<P: MaterialPipeline> Shaders<P> {
pub fn new(iter: impl IntoIterator<Item = (P::ShaderKey, AssetPath<'static>)>) -> Self {
Self::from_iter(iter)
}
pub fn extend(&mut self, other: Shaders<P>) {
self.shaders.extend(other.shaders.into_iter());
}
pub fn load(self, asset_server: &AssetServer) -> LoadedShaders<P> {
self.shaders
.into_iter()
.map(|(key, path)| (key, asset_server.load(path)))
.collect()
}
}
#[derive(Deref)]
pub struct LoadedShaders<P: MaterialPipeline> {
shaders: HashMap<P::ShaderKey, Handle<Shader>>,
}
impl<P: MaterialPipeline> FromIterator<(P::ShaderKey, Handle<Shader>)> for LoadedShaders<P> {
fn from_iter<T: IntoIterator<Item = (P::ShaderKey, Handle<Shader>)>>(iter: T) -> Self {
Self {
shaders: iter
.into_iter()
.map(|(key, path)| (key, path.into()))
.collect(),
}
}
}
impl<P: MaterialPipeline> LoadedShaders<P> {
pub fn new(iter: impl IntoIterator<Item = (P::ShaderKey, Handle<Shader>)>) -> Self {
Self::from_iter(iter)
}
pub fn extend(&mut self, other: LoadedShaders<P>) {
self.shaders.extend(other.shaders.into_iter());
}
}

View file

@ -0,0 +1,21 @@
use bevy_ecs::system::ReadOnlySystemParam;
use bevy_utils::HashMap;
use core::{hash::Hash, marker::PhantomData};
use crate::{material::Material, material_pipeline::MaterialPipeline};
pub trait Specialize {
type Key: Clone + Hash + Eq + Send + Sync;
type Item: Send + Sync;
}
pub type SpecializeMaterialContext<'a, M, P> =
<<P as MaterialPipeline>::Specializer<M> as SpecializeMaterial>::Context<'a>;
pub struct Specializer<T: Specialize> {
items: HashMap<T::Key, T::Item>,
}
impl<T: Specialize> Specializer<T> {}
pub struct SpecializedMaterial<M: Material<P>, P: MaterialPipeline>(PhantomData<fn(M, P)>);