diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index b9f4348..6af4399 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -25,6 +25,7 @@ use tracing::debug; use crate::geometry::{AttributeFormat, AttributeValue}; use crate::graphics::flush; +use crate::render::material::add_standard_materials; use crate::{ graphics::GraphicsPlugin, image::ImagePlugin, light::LightPlugin, render::command::DrawCommand, surface::SurfacePlugin, @@ -252,7 +253,13 @@ fn create_app(config: Config) -> App { LightPlugin, )); app.add_systems(First, (clear_transient_meshes, activate_cameras)) - .add_systems(Update, flush_draw_commands.before(AssetEventSystems)); + .add_systems( + Update, + ( + flush_draw_commands.before(AssetEventSystems), + add_standard_materials.after(flush_draw_commands), + ), + ); app } diff --git a/crates/processing_render/src/render/material.rs b/crates/processing_render/src/render/material.rs index fc2030c..bfdccf5 100644 --- a/crates/processing_render/src/render/material.rs +++ b/crates/processing_render/src/render/material.rs @@ -1,24 +1,68 @@ use bevy::{prelude::*, render::alpha::AlphaMode}; +use std::ops::Deref; +/// A component that holds an untyped handle to a material. This allows the main render loop +/// to be agnostic of the specific material types being used, and allows for dynamic material +/// creation based on the `MaterialKey`. +#[derive(Component, Deref)] +pub struct UntypedMaterial(pub UntypedHandle); + +/// Defines the current material for a batch, which can be used to determine when to flush the +/// current batch and start a new one. #[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct MaterialKey { - pub transparent: bool, - pub background_image: Option>, +pub enum MaterialKey { + Color { + transparent: bool, + background_image: Option>, + }, + Pbr {}, + Custom(Entity), } impl MaterialKey { - pub fn to_material(&self) -> StandardMaterial { - StandardMaterial { - base_color: Color::WHITE, - unlit: true, - cull_mode: None, - base_color_texture: self.background_image.clone(), - alpha_mode: if self.transparent { - AlphaMode::Blend - } else { - AlphaMode::Opaque - }, - ..default() + pub fn to_material( + &self, + standard_materials: &mut ResMut>, + ) -> UntypedHandle { + match self { + MaterialKey::Color { + background_image, + transparent, + } => { + let mat = StandardMaterial { + base_color: Color::WHITE, + unlit: true, + cull_mode: None, + base_color_texture: background_image.clone(), + alpha_mode: if *transparent { + AlphaMode::Blend + } else { + AlphaMode::Opaque + }, + ..default() + }; + standard_materials.add(mat).untyped() + } + MaterialKey::Pbr { .. } => { + todo!("implement pbr materials") + } + MaterialKey::Custom(_) => { + todo!("implement custom materials") + } + } + } +} + +/// A system that adds a `MeshMaterial3d` component to any entity with an `UntypedMaterial` that can +/// be typed as a `StandardMaterial`. +pub(crate) fn add_standard_materials( + mut commands: Commands, + meshes: Query<(Entity, &UntypedMaterial)>, +) { + for (entity, handle) in meshes.iter() { + let handle = handle.deref().clone(); + if let Ok(handle) = handle.try_typed::() { + commands.entity(entity).insert(MeshMaterial3d(handle)); } } } diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index 7871f14..e19188c 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -15,6 +15,7 @@ use material::MaterialKey; use primitive::{TessellationMode, empty_mesh}; use transform::TransformStack; +use crate::render::material::UntypedMaterial; use crate::{Flush, geometry::Geometry, image::Image, render::primitive::rect}; #[derive(Component)] @@ -153,15 +154,15 @@ pub fn flush_draw_commands( let mesh = create_ndc_background_quad(world_from_clip, color, false); let mesh_handle = res.meshes.add(mesh); - let material_key = MaterialKey { + let material_key = MaterialKey::Color { transparent: color.alpha() < 1.0, background_image: None, }; - let material_handle = res.materials.add(material_key.to_material()); + let material_handle = material_key.to_material(&mut res.materials); res.commands.spawn(( Mesh3d(mesh_handle), - MeshMaterial3d(material_handle), + UntypedMaterial(material_handle), BelongsToGraphics(batch.graphics_entity), Transform::IDENTITY, batch.render_layers.clone(), @@ -180,15 +181,15 @@ pub fn flush_draw_commands( let mesh = create_ndc_background_quad(world_from_clip, Color::WHITE, true); let mesh_handle = res.meshes.add(mesh); - let material_key = MaterialKey { + let material_key = MaterialKey::Color { transparent: false, background_image: Some(p_image.handle.clone()), }; - let material_handle = res.materials.add(material_key.to_material()); + let material_handle = material_key.to_material(&mut res.materials); res.commands.spawn(( Mesh3d(mesh_handle), - MeshMaterial3d(material_handle), + UntypedMaterial(material_handle), BelongsToGraphics(batch.graphics_entity), Transform::IDENTITY, batch.render_layers.clone(), @@ -214,12 +215,12 @@ pub fn flush_draw_commands( // TODO: Implement state based material API // https://github.com/processing/libprocessing/issues/10 - let material_key = MaterialKey { + let material_key = MaterialKey::Color { transparent: false, // TODO: detect from geometry colors background_image: None, }; - let material_handle = res.materials.add(material_key.to_material()); + let material_handle = material_key.to_material(&mut res.materials); let z_offset = -(batch.draw_index as f32 * 0.001); let mut transform = state.transform.to_bevy_transform(); @@ -227,7 +228,7 @@ pub fn flush_draw_commands( res.commands.spawn(( Mesh3d(geometry.handle.clone()), - MeshMaterial3d(material_handle), + UntypedMaterial(material_handle), BelongsToGraphics(batch.graphics_entity), transform, batch.render_layers.clone(), @@ -265,7 +266,7 @@ fn spawn_mesh(res: &mut RenderResources, batch: &mut BatchState, mesh: Mesh, z_o }; let mesh_handle = res.meshes.add(mesh); - let material_handle = res.materials.add(material_key.to_material()); + let material_handle = material_key.to_material(&mut res.materials); let (scale, rotation, translation) = batch.transform.to_scale_rotation_translation(); let transform = Transform { @@ -276,7 +277,7 @@ fn spawn_mesh(res: &mut RenderResources, batch: &mut BatchState, mesh: Mesh, z_o res.commands.spawn(( Mesh3d(mesh_handle), - MeshMaterial3d(material_handle), + UntypedMaterial(material_handle), BelongsToGraphics(batch.graphics_entity), transform, batch.render_layers.clone(), @@ -311,7 +312,7 @@ fn add_fill( let Some(color) = state.fill_color else { return; }; - let material_key = MaterialKey { + let material_key = MaterialKey::Color { transparent: state.fill_is_transparent(), background_image: None, }; @@ -335,7 +336,7 @@ fn add_stroke( return; }; let stroke_weight = state.stroke_weight; - let material_key = MaterialKey { + let material_key = MaterialKey::Color { transparent: state.stroke_is_transparent(), background_image: None, }; diff --git a/crates/processing_render/src/surface.rs b/crates/processing_render/src/surface.rs index 42eefb4..35601ee 100644 --- a/crates/processing_render/src/surface.rs +++ b/crates/processing_render/src/surface.rs @@ -19,6 +19,7 @@ //! - WebAssembly: `create_surface_web` #[cfg(any(target_os = "linux", target_arch = "wasm32"))] use std::ffi::c_void; +#[cfg(not(target_os = "windows"))] use std::ptr::NonNull; use bevy::{