 c79ec9cad6
			
		
	
	
		c79ec9cad6
		
	
	
	
	
		
			
			# Objective Fixes #3379 ## Solution The custom mesh pipelines needed to be specialized on each mesh's primitive topology, as done in `queue_meshes()` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
		
			
				
	
	
		
			237 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use bevy::{
 | |
|     core_pipeline::Transparent3d,
 | |
|     ecs::system::{lifetimeless::*, SystemParamItem},
 | |
|     pbr::{
 | |
|         DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup,
 | |
|         SetMeshViewBindGroup,
 | |
|     },
 | |
|     prelude::*,
 | |
|     reflect::TypeUuid,
 | |
|     render::{
 | |
|         camera::PerspectiveCameraBundle,
 | |
|         render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
 | |
|         render_component::ExtractComponentPlugin,
 | |
|         render_phase::{
 | |
|             AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
 | |
|             SetItemPipeline, TrackedRenderPass,
 | |
|         },
 | |
|         render_resource::*,
 | |
|         renderer::RenderDevice,
 | |
|         view::{ExtractedView, Msaa},
 | |
|         RenderApp, RenderStage,
 | |
|     },
 | |
| };
 | |
| use crevice::std140::{AsStd140, Std140};
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_plugin(CustomMaterialPlugin)
 | |
|         .add_startup_system(setup)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| /// set up a simple 3D scene
 | |
| fn setup(
 | |
|     mut commands: Commands,
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
|     mut materials: ResMut<Assets<CustomMaterial>>,
 | |
| ) {
 | |
|     // 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(),
 | |
|         Visibility::default(),
 | |
|         ComputedVisibility::default(),
 | |
|         materials.add(CustomMaterial {
 | |
|             color: Color::GREEN,
 | |
|         }),
 | |
|     ));
 | |
| 
 | |
|     // camera
 | |
|     commands.spawn_bundle(PerspectiveCameraBundle {
 | |
|         transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
 | |
|         ..Default::default()
 | |
|     });
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, TypeUuid)]
 | |
| #[uuid = "4ee9c363-1124-4113-890e-199d81b00281"]
 | |
| pub struct CustomMaterial {
 | |
|     color: Color,
 | |
| }
 | |
| 
 | |
| #[derive(Clone)]
 | |
| pub struct GpuCustomMaterial {
 | |
|     _buffer: Buffer,
 | |
|     bind_group: BindGroup,
 | |
| }
 | |
| 
 | |
| impl RenderAsset for CustomMaterial {
 | |
|     type ExtractedAsset = CustomMaterial;
 | |
|     type PreparedAsset = GpuCustomMaterial;
 | |
|     type Param = (SRes<RenderDevice>, SRes<CustomPipeline>);
 | |
|     fn extract_asset(&self) -> Self::ExtractedAsset {
 | |
|         self.clone()
 | |
|     }
 | |
| 
 | |
|     fn prepare_asset(
 | |
|         extracted_asset: Self::ExtractedAsset,
 | |
|         (render_device, custom_pipeline): &mut SystemParamItem<Self::Param>,
 | |
|     ) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
 | |
|         let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());
 | |
|         let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
 | |
|             contents: color.as_std140().as_bytes(),
 | |
|             label: None,
 | |
|             usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
 | |
|         });
 | |
|         let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
 | |
|             entries: &[BindGroupEntry {
 | |
|                 binding: 0,
 | |
|                 resource: buffer.as_entire_binding(),
 | |
|             }],
 | |
|             label: None,
 | |
|             layout: &custom_pipeline.material_layout,
 | |
|         });
 | |
| 
 | |
|         Ok(GpuCustomMaterial {
 | |
|             _buffer: buffer,
 | |
|             bind_group,
 | |
|         })
 | |
|     }
 | |
| }
 | |
| pub struct CustomMaterialPlugin;
 | |
| 
 | |
| impl Plugin for CustomMaterialPlugin {
 | |
|     fn build(&self, app: &mut App) {
 | |
|         app.add_asset::<CustomMaterial>()
 | |
|             .add_plugin(ExtractComponentPlugin::<Handle<CustomMaterial>>::default())
 | |
|             .add_plugin(RenderAssetPlugin::<CustomMaterial>::default());
 | |
|         app.sub_app(RenderApp)
 | |
|             .add_render_command::<Transparent3d, DrawCustom>()
 | |
|             .init_resource::<CustomPipeline>()
 | |
|             .init_resource::<SpecializedPipelines<CustomPipeline>>()
 | |
|             .add_system_to_stage(RenderStage::Queue, queue_custom);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub struct CustomPipeline {
 | |
|     mesh_pipeline: MeshPipeline,
 | |
|     material_layout: BindGroupLayout,
 | |
|     shader: Handle<Shader>,
 | |
| }
 | |
| 
 | |
| impl SpecializedPipeline for CustomPipeline {
 | |
|     type Key = MeshPipelineKey;
 | |
| 
 | |
|     fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
 | |
|         let mut descriptor = self.mesh_pipeline.specialize(key);
 | |
|         descriptor.fragment.as_mut().unwrap().shader = self.shader.clone();
 | |
|         descriptor.layout = Some(vec![
 | |
|             self.mesh_pipeline.view_layout.clone(),
 | |
|             self.material_layout.clone(),
 | |
|             self.mesh_pipeline.mesh_layout.clone(),
 | |
|         ]);
 | |
|         descriptor
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl FromWorld for CustomPipeline {
 | |
|     fn from_world(world: &mut World) -> Self {
 | |
|         let asset_server = world.get_resource::<AssetServer>().unwrap();
 | |
|         // Watch for changes, allowing for hot shader reloading
 | |
|         // Try changing custom_material.wgsl while the app is running!
 | |
|         asset_server.watch_for_changes().unwrap();
 | |
| 
 | |
|         let render_device = world.get_resource::<RenderDevice>().unwrap();
 | |
|         let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
 | |
|             entries: &[BindGroupLayoutEntry {
 | |
|                 binding: 0,
 | |
|                 visibility: ShaderStages::FRAGMENT,
 | |
|                 ty: BindingType::Buffer {
 | |
|                     ty: BufferBindingType::Uniform,
 | |
|                     has_dynamic_offset: false,
 | |
|                     min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64),
 | |
|                 },
 | |
|                 count: None,
 | |
|             }],
 | |
|             label: None,
 | |
|         });
 | |
| 
 | |
|         CustomPipeline {
 | |
|             mesh_pipeline: world.get_resource::<MeshPipeline>().unwrap().clone(),
 | |
|             shader: asset_server.load("shaders/custom_material.wgsl"),
 | |
|             material_layout,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(clippy::too_many_arguments)]
 | |
| pub fn queue_custom(
 | |
|     transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
 | |
|     materials: Res<RenderAssets<CustomMaterial>>,
 | |
|     render_meshes: Res<RenderAssets<Mesh>>,
 | |
|     custom_pipeline: Res<CustomPipeline>,
 | |
|     mut pipeline_cache: ResMut<RenderPipelineCache>,
 | |
|     mut specialized_pipelines: ResMut<SpecializedPipelines<CustomPipeline>>,
 | |
|     msaa: Res<Msaa>,
 | |
|     material_meshes: Query<(Entity, &Handle<CustomMaterial>, &Handle<Mesh>, &MeshUniform)>,
 | |
|     mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
 | |
| ) {
 | |
|     let draw_custom = transparent_3d_draw_functions
 | |
|         .read()
 | |
|         .get_id::<DrawCustom>()
 | |
|         .unwrap();
 | |
|     let 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, material_handle, mesh_handle, mesh_uniform) in material_meshes.iter() {
 | |
|             if materials.contains_key(material_handle) {
 | |
|                 if let Some(mesh) = render_meshes.get(mesh_handle) {
 | |
|                     let key =
 | |
|                         key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
 | |
|                     transparent_phase.add(Transparent3d {
 | |
|                         entity,
 | |
|                         pipeline: specialized_pipelines.specialize(
 | |
|                             &mut pipeline_cache,
 | |
|                             &custom_pipeline,
 | |
|                             key,
 | |
|                         ),
 | |
|                         draw_function: draw_custom,
 | |
|                         distance: view_row_2.dot(mesh_uniform.transform.col(3)),
 | |
|                     });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| type DrawCustom = (
 | |
|     SetItemPipeline,
 | |
|     SetMeshViewBindGroup<0>,
 | |
|     SetCustomMaterialBindGroup,
 | |
|     SetMeshBindGroup<2>,
 | |
|     DrawMesh,
 | |
| );
 | |
| 
 | |
| struct SetCustomMaterialBindGroup;
 | |
| impl EntityRenderCommand for SetCustomMaterialBindGroup {
 | |
|     type Param = (
 | |
|         SRes<RenderAssets<CustomMaterial>>,
 | |
|         SQuery<Read<Handle<CustomMaterial>>>,
 | |
|     );
 | |
|     fn render<'w>(
 | |
|         _view: Entity,
 | |
|         item: Entity,
 | |
|         (materials, query): SystemParamItem<'w, '_, Self::Param>,
 | |
|         pass: &mut TrackedRenderPass<'w>,
 | |
|     ) -> RenderCommandResult {
 | |
|         let material_handle = query.get(item).unwrap();
 | |
|         let material = materials.into_inner().get(material_handle).unwrap();
 | |
|         pass.set_bind_group(1, &material.bind_group, &[]);
 | |
|         RenderCommandResult::Success
 | |
|     }
 | |
| }
 |