use bevy::{ core_pipeline::Transparent3d, pbr::{ DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup, }, prelude::*, render::{ mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ PipelineCache, RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, view::ExtractedView, RenderApp, RenderStage, }, }; pub struct IsRedPlugin; impl Plugin for IsRedPlugin { fn build(&self, app: &mut App) { app.add_plugin(ExtractComponentPlugin::::default()); app.sub_app_mut(RenderApp) .add_render_command::() .init_resource::() .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom); } } fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(IsRedPlugin) .add_startup_system(setup) .run(); } #[derive(Component, Hash, PartialEq, Eq, Copy, Clone)] struct IsRed(bool); impl ExtractComponent for IsRed { type Query = &'static IsRed; type Filter = (); fn extract_component(item: bevy::ecs::query::QueryItem) -> Self { *item } } /// set up a simple 3D scene fn setup(mut commands: Commands, mut meshes: ResMut>) { // red cube commands.spawn().insert_bundle(( meshes.add(Mesh::from(shape::Cube { size: 1.0 })), IsRed(true), Transform::from_xyz(-1.0, 0.5, 0.0), GlobalTransform::default(), Visibility::default(), ComputedVisibility::default(), )); // blue cube commands.spawn().insert_bundle(( meshes.add(Mesh::from(shape::Cube { size: 1.0 })), IsRed(false), Transform::from_xyz(1.0, 0.5, 0.0), GlobalTransform::default(), 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() }); } struct IsRedPipeline { mesh_pipeline: MeshPipeline, shader: Handle, } impl FromWorld for IsRedPipeline { fn from_world(world: &mut World) -> Self { let asset_server = world.resource::(); let mesh_pipeline = world.resource::(); let shader = asset_server.load("shaders/shader_defs.wgsl"); IsRedPipeline { mesh_pipeline: mesh_pipeline.clone(), shader, } } } impl SpecializedMeshPipeline for IsRedPipeline { type Key = (IsRed, MeshPipelineKey); fn specialize( &self, (is_red, pbr_pipeline_key): Self::Key, layout: &MeshVertexBufferLayout, ) -> Result { let mut shader_defs = Vec::new(); if is_red.0 { shader_defs.push("IS_RED".to_string()); } let mut descriptor = self.mesh_pipeline.specialize(pbr_pipeline_key, layout)?; descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.shader_defs = shader_defs.clone(); let fragment = descriptor.fragment.as_mut().unwrap(); fragment.shader = self.shader.clone(); fragment.shader_defs = shader_defs; descriptor.layout = Some(vec![ self.mesh_pipeline.view_layout.clone(), self.mesh_pipeline.mesh_layout.clone(), ]); Ok(descriptor) } } type DrawIsRed = ( SetItemPipeline, SetMeshViewBindGroup<0>, SetMeshBindGroup<1>, DrawMesh, ); #[allow(clippy::too_many_arguments)] fn queue_custom( transparent_3d_draw_functions: Res>, render_meshes: Res>, custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, mut pipeline_cache: ResMut, material_meshes: Query<(Entity, &Handle, &MeshUniform, &IsRed)>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { let draw_custom = transparent_3d_draw_functions .read() .get_id::() .unwrap(); let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); 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_handle, mesh_uniform, is_red) in material_meshes.iter() { if let Some(mesh) = render_meshes.get(mesh_handle) { let key = msaa_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); let pipeline = pipelines .specialize( &mut pipeline_cache, &custom_pipeline, (*is_red, key), &mesh.layout, ) .unwrap(); transparent_phase.add(Transparent3d { entity, pipeline, draw_function: draw_custom, distance: view_row_2.dot(mesh_uniform.transform.col(3)), }); } } } }