use bevy::{ core_pipeline::Transparent3d, ecs::system::{lifetimeless::SRes, SystemParamItem}, pbr::{ DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup, }, prelude::*, render::{ mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_phase::{ AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass, }, render_resource::*, renderer::{RenderDevice, RenderQueue}, view::{ComputedVisibility, ExtractedView, Msaa, Visibility}, RenderApp, RenderStage, }, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(CustomMaterialPlugin) .add_startup_system(setup) .run(); } fn setup(mut commands: Commands, mut meshes: ResMut>) { // cube commands.spawn().insert_bundle(( meshes.add(Mesh::from(shape::Cube { size: 1.0 })), Transform::from_xyz(0.0, 0.5, 0.0), GlobalTransform::default(), CustomMaterial, Visibility::default(), ComputedVisibility::default(), )); // camera commands.spawn_bundle(PerspectiveCameraBundle { transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..Default::default() }); } #[derive(Component)] struct CustomMaterial; pub struct CustomMaterialPlugin; impl Plugin for CustomMaterialPlugin { fn build(&self, app: &mut App) { let render_device = app.world.resource::(); let buffer = render_device.create_buffer(&BufferDescriptor { label: Some("time uniform buffer"), size: std::mem::size_of::() as u64, usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, mapped_at_creation: false, }); app.sub_app_mut(RenderApp) .add_render_command::() .insert_resource(TimeMeta { buffer, bind_group: None, }) .init_resource::() .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_time) .add_system_to_stage(RenderStage::Extract, extract_custom_material) .add_system_to_stage(RenderStage::Prepare, prepare_time) .add_system_to_stage(RenderStage::Queue, queue_custom) .add_system_to_stage(RenderStage::Queue, queue_time_bind_group); } } // extract the `CustomMaterial` component into the render world fn extract_custom_material( mut commands: Commands, mut previous_len: Local, mut query: Query>, ) { let mut values = Vec::with_capacity(*previous_len); for entity in query.iter_mut() { values.push((entity, (CustomMaterial,))); } *previous_len = values.len(); commands.insert_or_spawn_batch(values); } // add each entity with a mesh and a `CustomMaterial` to every view's `Transparent3d` render phase using the `CustomPipeline` #[allow(clippy::too_many_arguments)] fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, mut pipeline_cache: ResMut, render_meshes: Res>, material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { let draw_custom = transparent_3d_draw_functions .read() .get_id::() .unwrap(); let key = MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { if let Some(mesh) = render_meshes.get(mesh_handle) { let pipeline = pipelines .specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout) .unwrap(); transparent_phase.add(Transparent3d { entity, pipeline, draw_function: draw_custom, distance: view_row_2.dot(mesh_uniform.transform.col(3)), }); } } } } #[derive(Default)] struct ExtractedTime { seconds_since_startup: f32, } // extract the passed time into a resource in the render world fn extract_time(mut commands: Commands, time: Res