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_data;
pub mod material_pipeline;
pub mod shaders;
pub mod specialize;
pub mod prelude {
pub use super::material::{Material, MaterialPlugin};
@ -21,14 +23,4 @@ mod tests {
pub struct 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},
world::{FromWorld, World},
};
use bevy_utils::HashMap;
use core::marker::PhantomData;
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_ecs::system::{Query, ResMut, Resource};
use bevy_render::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin},
render_resource::{AsBindGroup, AsBindGroupError, BindGroupLayout, PreparedBindGroup, Shader},
render_resource::{AsBindGroup, AsBindGroupError, BindGroupLayout, PreparedBindGroup},
renderer::RenderDevice,
sync_world::{MainEntity, MainEntityHashMap},
view::ViewVisibility,
Extract, ExtractSchedule, RenderApp,
};
use crate::component::MaterialComponent;
use crate::material_pipeline::MaterialPipeline;
use crate::{
component::MaterialComponent,
shaders::{LoadedShaders, Shaders},
};
pub enum SpecializeMaterialError {}
@ -34,24 +36,6 @@ pub trait Material<P: MaterialPipeline>: BaseMaterial {
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)>);
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> {
#[deref]
properties: R::MaterialProperties,
_data: PhantomData<M>,
_data: PhantomData<fn(M)>,
}
impl<M: Material<R>, R: MaterialPipeline> MaterialProperties<M, R> {
@ -191,19 +175,18 @@ impl<M: BaseMaterial> FromWorld for MaterialLayout<M> {
#[derive(Deref, Resource)]
pub struct MaterialShaders<M: Material<P>, P: MaterialPipeline> {
#[deref]
shaders: HashMap<P::ShaderKey, Handle<Shader>>,
shaders: LoadedShaders<P>,
_data: PhantomData<fn(M)>,
}
impl<M: Material<P>, P: MaterialPipeline> FromWorld for MaterialShaders<M, P> {
fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>();
let mut shaders = P::default_shaders();
shaders.extend(M::shaders());
Self {
shaders: M::shaders()
.shaders
.into_iter()
.map(|(key, path)| (key, asset_server.load(path)))
.collect(),
shaders: shaders.load(asset_server),
_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> {
pub fn get(
&self,
main_entity: MainEntity,
id: AssetId<M>,
) -> Option<PreparedMaterialInstance<M, P>> {
pub fn get(&self, main_entity: MainEntity, id: AssetId<M>) -> Option<MaterialInstance<M, P>> {
let bind_group = self.bind_groups.get(id)?;
let properties = self.properties.get(id)?;
Some(PreparedMaterialInstance {
Some(MaterialInstance {
main_entity,
layout: &self.layout,
shaders: &self.shaders,
@ -42,14 +38,14 @@ impl<'w, M: Material<P>, P: MaterialPipeline> MaterialData<'w, M, P> {
pub fn iter<'a>(
&'a self,
instances: &'a MaterialInstances<M, P>,
) -> impl Iterator<Item = PreparedMaterialInstance<'a, M, P>> + 'a {
) -> impl Iterator<Item = MaterialInstance<'a, M, P>> {
instances
.iter()
.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 properties: &'a P::MaterialProperties,
pub shaders: &'a MaterialShaders<M, P>,

View file

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