Skip to content

Commit

Permalink
Introduce ShaderModel.
Browse files Browse the repository at this point in the history
  • Loading branch information
tychedelia committed Sep 9, 2024
1 parent e6c19ab commit b467f83
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 267 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"bevy_nannou",
"bevy_nannou_derive",
"bevy_nannou_draw",
"bevy_nannou_isf",
"bevy_nannou_video",
Expand Down
16 changes: 16 additions & 0 deletions bevy_nannou_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "bevy_nannou_derive"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
syn = { version = "1", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"

[dev-dependencies]
bevy = { workspace = true }
bevy_nannou_draw = { path = "../bevy_nannou_draw" }
75 changes: 75 additions & 0 deletions bevy_nannou_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, Attribute, ItemStruct, Lit, Meta, NestedMeta};

#[proc_macro_attribute]
pub fn shader_model(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as ItemStruct);
let attrs = parse_macro_input!(attr as syn::AttributeArgs);

let (vertex_shader, fragment_shader) = parse_shader_attributes(&attrs);

let name = &input.ident;
let generics = &input.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let vertex_shader_impl = vertex_shader
.map(|path| quote! { #path.into() })
.unwrap_or_else(|| quote! { ::nannou::prelude::ShaderRef::Default });

let fragment_shader_impl = fragment_shader
.map(|path| quote! { #path.into() })
.unwrap_or_else(|| quote! { ::nannou::prelude::ShaderRef::Default });

// Add derive attributes
input
.attrs
.push(parse_quote!(#[derive(Asset, TypePath, AsBindGroup, Debug, Clone, Default)]));

let expanded = quote! {
#input

impl #impl_generics ::nannou::prelude::render::ShaderModel for #name #ty_generics #where_clause {
fn vertex_shader() -> ::nannou::prelude::ShaderRef {
#vertex_shader_impl
}

fn fragment_shader() -> ::nannou::prelude::ShaderRef {
#fragment_shader_impl
}
}

impl #impl_generics ::nannou::prelude::Material for #name #ty_generics #where_clause {
fn vertex_shader() -> ::nannou::prelude::ShaderRef {
<Self as ::nannou::prelude::render::ShaderModel>::vertex_shader()
}

fn fragment_shader() -> ::nannou::prelude::ShaderRef {
<Self as ::nannou::prelude::render::ShaderModel>::fragment_shader()
}
}
};

TokenStream::from(expanded)
}

fn parse_shader_attributes(attrs: &[syn::NestedMeta]) -> (Option<String>, Option<String>) {
let mut vertex_shader = None;
let mut fragment_shader = None;

for attr in attrs {
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr {
if nv.path.is_ident("vertex") {
if let Lit::Str(lit) = &nv.lit {
vertex_shader = Some(lit.value());
}
} else if nv.path.is_ident("fragment") {
if let Lit::Str(lit) = &nv.lit {
fragment_shader = Some(lit.value());
}
}
}
}

(vertex_shader, fragment_shader)
}
60 changes: 60 additions & 0 deletions bevy_nannou_derive/tests/shader_model_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use bevy::render::render_resource::ShaderRef;
use bevy_nannou_derive::shader_model;
use bevy_nannou_draw::render::ShaderModel;

#[shader_model]
struct TestMaterial {}

#[test]
fn test_default_shaders() {
assert!(matches!(TestMaterial::vertex_shader(), ShaderRef::Default));
assert!(matches!(
TestMaterial::fragment_shader(),
ShaderRef::Default
));
}

#[shader_model(vertex = "custom_vertex.wgsl")]
struct TestVertexMaterial {}

#[test]
fn test_custom_vertex_shader() {
assert!(matches!(
TestVertexMaterial::vertex_shader(),
ShaderRef::Path(_)
));
assert!(matches!(
TestVertexMaterial::fragment_shader(),
ShaderRef::Default
));
}

#[shader_model(fragment = "custom_fragment.wgsl")]
struct TestFragmentMaterial {}

#[test]
fn test_custom_fragment_shader() {
assert!(matches!(
TestFragmentMaterial::vertex_shader(),
ShaderRef::Default
));
assert!(matches!(
TestFragmentMaterial::fragment_shader(),
ShaderRef::Path(_)
));
}

#[shader_model(vertex = "custom_vertex.wgsl", fragment = "custom_fragment.wgsl")]
struct TestBothMaterial {}

#[test]
fn test_both_custom_shaders() {
assert!(matches!(
TestBothMaterial::vertex_shader(),
ShaderRef::Path(_)
));
assert!(matches!(
TestBothMaterial::fragment_shader(),
ShaderRef::Path(_)
));
}
45 changes: 0 additions & 45 deletions bevy_nannou_draw/src/draw/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,51 +140,6 @@ where
self.finish_inner()
}

/// Set the material's fragment shader for the drawing. Note: this shader must have
/// been initialized during application setup.
#[cfg(feature = "nightly")]
pub fn fragment_shader<const FS: &'static str>(
mut self,
) -> Drawing<'a, T, ExtendedNannouMaterial<"", FS>> {
self.finish_on_drop = false;

let Drawing {
ref draw,
index,
material_index,
..
} = self;

let state = draw.state.clone();
let new_id = UntypedAssetId::Uuid {
type_id: TypeId::of::<ExtendedNannouMaterial<"", FS>>(),
uuid: Uuid::new_v4(),
};

let material: ExtendedNannouMaterial<"", FS> = Default::default();
let mut state = state.write().unwrap();
state.materials.insert(new_id.clone(), Box::new(material));
// Mark the last material as the new material so that further drawings use the same material
// as the parent draw ref.
state.last_material = Some(new_id.clone());

let draw = Draw {
state: draw.state.clone(),
context: draw.context.clone(),
material: new_id.clone(),
window: draw.window,
_material: Default::default(),
};

Drawing::<'a, T, ExtendedMaterial<StandardMaterial, NannouMaterial<"", FS>>> {
draw: DrawRef::Owned(draw),
index,
material_index,
finish_on_drop: true,
_ty: PhantomData,
}
}

// Map the the parent's material to a new material type, taking ownership over the
// draw instance clone.
pub fn map_material<F>(mut self, map: F) -> Drawing<'a, T, M>
Expand Down
Loading

0 comments on commit b467f83

Please sign in to comment.