1use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight};
2#[cfg(feature = "meshlet")]
3use crate::meshlet::{
4 prepare_material_meshlet_meshes_main_opaque_pass, queue_material_meshlet_meshes,
5 InstanceManager,
6};
7use crate::*;
8use bevy_asset::{Asset, AssetId, AssetServer};
9use bevy_core_pipeline::{
10 core_3d::{
11 AlphaMask3d, Camera3d, Opaque3d, Opaque3dBinKey, ScreenSpaceTransmissionQuality,
12 Transmissive3d, Transparent3d,
13 },
14 oit::OrderIndependentTransparencySettings,
15 prepass::{
16 DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, OpaqueNoLightmap3dBinKey,
17 },
18 tonemapping::{DebandDither, Tonemapping},
19};
20use bevy_derive::{Deref, DerefMut};
21use bevy_ecs::{
22 prelude::*,
23 system::{lifetimeless::SRes, SystemParamItem},
24};
25use bevy_reflect::std_traits::ReflectDefault;
26use bevy_reflect::Reflect;
27use bevy_render::sync_world::MainEntityHashMap;
28use bevy_render::view::RenderVisibleEntities;
29use bevy_render::{
30 camera::TemporalJitter,
31 extract_resource::ExtractResource,
32 mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh},
33 render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
34 render_phase::*,
35 render_resource::*,
36 renderer::RenderDevice,
37 view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility},
38 Extract,
39};
40use bevy_utils::tracing::error;
41use core::{
42 hash::Hash,
43 marker::PhantomData,
44 num::NonZero,
45 sync::atomic::{AtomicU32, Ordering},
46};
47
48pub trait Material: Asset + AsBindGroup + Clone + Sized {
117 fn vertex_shader() -> ShaderRef {
120 ShaderRef::Default
121 }
122
123 #[allow(unused_variables)]
126 fn fragment_shader() -> ShaderRef {
127 ShaderRef::Default
128 }
129
130 #[inline]
132 fn alpha_mode(&self) -> AlphaMode {
133 AlphaMode::Opaque
134 }
135
136 #[inline]
140 fn opaque_render_method(&self) -> OpaqueRendererMethod {
141 OpaqueRendererMethod::Forward
142 }
143
144 #[inline]
145 fn depth_bias(&self) -> f32 {
149 0.0
150 }
151
152 #[inline]
153 fn reads_view_transmission_texture(&self) -> bool {
158 false
159 }
160
161 fn prepass_vertex_shader() -> ShaderRef {
167 ShaderRef::Default
168 }
169
170 #[allow(unused_variables)]
176 fn prepass_fragment_shader() -> ShaderRef {
177 ShaderRef::Default
178 }
179
180 fn deferred_vertex_shader() -> ShaderRef {
183 ShaderRef::Default
184 }
185
186 #[allow(unused_variables)]
189 fn deferred_fragment_shader() -> ShaderRef {
190 ShaderRef::Default
191 }
192
193 #[allow(unused_variables)]
200 #[cfg(feature = "meshlet")]
201 fn meshlet_mesh_fragment_shader() -> ShaderRef {
202 ShaderRef::Default
203 }
204
205 #[allow(unused_variables)]
212 #[cfg(feature = "meshlet")]
213 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
214 ShaderRef::Default
215 }
216
217 #[allow(unused_variables)]
224 #[cfg(feature = "meshlet")]
225 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
226 ShaderRef::Default
227 }
228
229 #[allow(unused_variables)]
232 #[inline]
233 fn specialize(
234 pipeline: &MaterialPipeline<Self>,
235 descriptor: &mut RenderPipelineDescriptor,
236 layout: &MeshVertexBufferLayoutRef,
237 key: MaterialPipelineKey<Self>,
238 ) -> Result<(), SpecializedMeshPipelineError> {
239 Ok(())
240 }
241}
242
243pub struct MaterialPlugin<M: Material> {
246 pub prepass_enabled: bool,
252 pub shadows_enabled: bool,
254 pub _marker: PhantomData<M>,
255}
256
257impl<M: Material> Default for MaterialPlugin<M> {
258 fn default() -> Self {
259 Self {
260 prepass_enabled: true,
261 shadows_enabled: true,
262 _marker: Default::default(),
263 }
264 }
265}
266
267impl<M: Material> Plugin for MaterialPlugin<M>
268where
269 M::Data: PartialEq + Eq + Hash + Clone,
270{
271 fn build(&self, app: &mut App) {
272 app.init_asset::<M>()
273 .register_type::<MeshMaterial3d<M>>()
274 .add_plugins(RenderAssetPlugin::<PreparedMaterial<M>>::default());
275
276 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
277 render_app
278 .init_resource::<DrawFunctions<Shadow>>()
279 .init_resource::<RenderMaterialInstances<M>>()
280 .add_render_command::<Shadow, DrawPrepass<M>>()
281 .add_render_command::<Transmissive3d, DrawMaterial<M>>()
282 .add_render_command::<Transparent3d, DrawMaterial<M>>()
283 .add_render_command::<Opaque3d, DrawMaterial<M>>()
284 .add_render_command::<AlphaMask3d, DrawMaterial<M>>()
285 .init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
286 .add_systems(ExtractSchedule, extract_mesh_materials::<M>)
287 .add_systems(
288 Render,
289 queue_material_meshes::<M>
290 .in_set(RenderSet::QueueMeshes)
291 .after(prepare_assets::<PreparedMaterial<M>>),
292 );
293
294 if self.shadows_enabled {
295 render_app.add_systems(
296 Render,
297 queue_shadows::<M>
298 .in_set(RenderSet::QueueMeshes)
299 .after(prepare_assets::<PreparedMaterial<M>>),
300 );
301 }
302
303 #[cfg(feature = "meshlet")]
304 render_app.add_systems(
305 Render,
306 queue_material_meshlet_meshes::<M>
307 .in_set(RenderSet::QueueMeshes)
308 .run_if(resource_exists::<InstanceManager>),
309 );
310
311 #[cfg(feature = "meshlet")]
312 render_app.add_systems(
313 Render,
314 prepare_material_meshlet_meshes_main_opaque_pass::<M>
315 .in_set(RenderSet::QueueMeshes)
316 .after(prepare_assets::<PreparedMaterial<M>>)
317 .before(queue_material_meshlet_meshes::<M>)
318 .run_if(resource_exists::<InstanceManager>),
319 );
320 }
321
322 if self.shadows_enabled || self.prepass_enabled {
323 app.add_plugins(PrepassPipelinePlugin::<M>::default());
325 }
326
327 if self.prepass_enabled {
328 app.add_plugins(PrepassPlugin::<M>::default());
329 }
330 }
331
332 fn finish(&self, app: &mut App) {
333 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
334 render_app.init_resource::<MaterialPipeline<M>>();
335 }
336 }
337}
338
339pub struct MaterialPipelineKey<M: Material> {
341 pub mesh_key: MeshPipelineKey,
342 pub bind_group_data: M::Data,
343}
344
345impl<M: Material> Eq for MaterialPipelineKey<M> where M::Data: PartialEq {}
346
347impl<M: Material> PartialEq for MaterialPipelineKey<M>
348where
349 M::Data: PartialEq,
350{
351 fn eq(&self, other: &Self) -> bool {
352 self.mesh_key == other.mesh_key && self.bind_group_data == other.bind_group_data
353 }
354}
355
356impl<M: Material> Clone for MaterialPipelineKey<M>
357where
358 M::Data: Clone,
359{
360 fn clone(&self) -> Self {
361 Self {
362 mesh_key: self.mesh_key,
363 bind_group_data: self.bind_group_data.clone(),
364 }
365 }
366}
367
368impl<M: Material> Hash for MaterialPipelineKey<M>
369where
370 M::Data: Hash,
371{
372 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
373 self.mesh_key.hash(state);
374 self.bind_group_data.hash(state);
375 }
376}
377
378#[derive(Resource)]
380pub struct MaterialPipeline<M: Material> {
381 pub mesh_pipeline: MeshPipeline,
382 pub material_layout: BindGroupLayout,
383 pub vertex_shader: Option<Handle<Shader>>,
384 pub fragment_shader: Option<Handle<Shader>>,
385 pub marker: PhantomData<M>,
386}
387
388impl<M: Material> Clone for MaterialPipeline<M> {
389 fn clone(&self) -> Self {
390 Self {
391 mesh_pipeline: self.mesh_pipeline.clone(),
392 material_layout: self.material_layout.clone(),
393 vertex_shader: self.vertex_shader.clone(),
394 fragment_shader: self.fragment_shader.clone(),
395 marker: PhantomData,
396 }
397 }
398}
399
400impl<M: Material> SpecializedMeshPipeline for MaterialPipeline<M>
401where
402 M::Data: PartialEq + Eq + Hash + Clone,
403{
404 type Key = MaterialPipelineKey<M>;
405
406 fn specialize(
407 &self,
408 key: Self::Key,
409 layout: &MeshVertexBufferLayoutRef,
410 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
411 let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
412 if let Some(vertex_shader) = &self.vertex_shader {
413 descriptor.vertex.shader = vertex_shader.clone();
414 }
415
416 if let Some(fragment_shader) = &self.fragment_shader {
417 descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
418 }
419
420 descriptor.layout.insert(2, self.material_layout.clone());
421
422 M::specialize(self, &mut descriptor, layout, key)?;
423 Ok(descriptor)
424 }
425}
426
427impl<M: Material> FromWorld for MaterialPipeline<M> {
428 fn from_world(world: &mut World) -> Self {
429 let asset_server = world.resource::<AssetServer>();
430 let render_device = world.resource::<RenderDevice>();
431
432 MaterialPipeline {
433 mesh_pipeline: world.resource::<MeshPipeline>().clone(),
434 material_layout: M::bind_group_layout(render_device),
435 vertex_shader: match M::vertex_shader() {
436 ShaderRef::Default => None,
437 ShaderRef::Handle(handle) => Some(handle),
438 ShaderRef::Path(path) => Some(asset_server.load(path)),
439 },
440 fragment_shader: match M::fragment_shader() {
441 ShaderRef::Default => None,
442 ShaderRef::Handle(handle) => Some(handle),
443 ShaderRef::Path(path) => Some(asset_server.load(path)),
444 },
445 marker: PhantomData,
446 }
447 }
448}
449
450type DrawMaterial<M> = (
451 SetItemPipeline,
452 SetMeshViewBindGroup<0>,
453 SetMeshBindGroup<1>,
454 SetMaterialBindGroup<M, 2>,
455 DrawMesh,
456);
457
458pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
460impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
461 type Param = (
462 SRes<RenderAssets<PreparedMaterial<M>>>,
463 SRes<RenderMaterialInstances<M>>,
464 );
465 type ViewQuery = ();
466 type ItemQuery = ();
467
468 #[inline]
469 fn render<'w>(
470 item: &P,
471 _view: (),
472 _item_query: Option<()>,
473 (materials, material_instances): SystemParamItem<'w, '_, Self::Param>,
474 pass: &mut TrackedRenderPass<'w>,
475 ) -> RenderCommandResult {
476 let materials = materials.into_inner();
477 let material_instances = material_instances.into_inner();
478
479 let Some(material_asset_id) = material_instances.get(&item.main_entity()) else {
480 return RenderCommandResult::Skip;
481 };
482 let Some(material) = materials.get(*material_asset_id) else {
483 return RenderCommandResult::Skip;
484 };
485 pass.set_bind_group(I, &material.bind_group, &[]);
486 RenderCommandResult::Success
487 }
488}
489
490#[derive(Resource, Deref, DerefMut)]
492pub struct RenderMaterialInstances<M: Material>(pub MainEntityHashMap<AssetId<M>>);
493
494impl<M: Material> Default for RenderMaterialInstances<M> {
495 fn default() -> Self {
496 Self(Default::default())
497 }
498}
499
500pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
501 match alpha_mode {
502 AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
505 AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
506 AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
507 AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
508 AlphaMode::AlphaToCoverage => match *msaa {
509 Msaa::Off => MeshPipelineKey::MAY_DISCARD,
510 _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
511 },
512 _ => MeshPipelineKey::NONE,
513 }
514}
515
516pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
517 match tonemapping {
518 Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
519 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
520 Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
521 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
522 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
523 Tonemapping::SomewhatBoringDisplayTransform => {
524 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
525 }
526 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
527 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
528 }
529}
530
531pub const fn screen_space_specular_transmission_pipeline_key(
532 screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
533) -> MeshPipelineKey {
534 match screen_space_transmissive_blur_quality {
535 ScreenSpaceTransmissionQuality::Low => {
536 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
537 }
538 ScreenSpaceTransmissionQuality::Medium => {
539 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
540 }
541 ScreenSpaceTransmissionQuality::High => {
542 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
543 }
544 ScreenSpaceTransmissionQuality::Ultra => {
545 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
546 }
547 }
548}
549
550pub fn extract_mesh_materials<M: Material>(
551 mut material_instances: ResMut<RenderMaterialInstances<M>>,
552 query: Extract<Query<(Entity, &ViewVisibility, &MeshMaterial3d<M>)>>,
553) {
554 material_instances.clear();
555
556 for (entity, view_visibility, material) in &query {
557 if view_visibility.get() {
558 material_instances.insert(entity.into(), material.id());
559 }
560 }
561}
562
563#[allow(clippy::too_many_arguments)]
566pub fn queue_material_meshes<M: Material>(
567 (
568 opaque_draw_functions,
569 alpha_mask_draw_functions,
570 transmissive_draw_functions,
571 transparent_draw_functions,
572 ): (
573 Res<DrawFunctions<Opaque3d>>,
574 Res<DrawFunctions<AlphaMask3d>>,
575 Res<DrawFunctions<Transmissive3d>>,
576 Res<DrawFunctions<Transparent3d>>,
577 ),
578 material_pipeline: Res<MaterialPipeline<M>>,
579 mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
580 pipeline_cache: Res<PipelineCache>,
581 render_meshes: Res<RenderAssets<RenderMesh>>,
582 render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
583 render_mesh_instances: Res<RenderMeshInstances>,
584 render_material_instances: Res<RenderMaterialInstances<M>>,
585 render_lightmaps: Res<RenderLightmaps>,
586 render_visibility_ranges: Res<RenderVisibilityRanges>,
587 mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
588 mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
589 mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
590 mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
591 views: Query<(
592 Entity,
593 &ExtractedView,
594 &RenderVisibleEntities,
595 &Msaa,
596 Option<&Tonemapping>,
597 Option<&DebandDither>,
598 Option<&ShadowFilteringMethod>,
599 Has<ScreenSpaceAmbientOcclusion>,
600 (
601 Has<NormalPrepass>,
602 Has<DepthPrepass>,
603 Has<MotionVectorPrepass>,
604 Has<DeferredPrepass>,
605 ),
606 Option<&Camera3d>,
607 Has<TemporalJitter>,
608 Option<&Projection>,
609 (
610 Has<RenderViewLightProbes<EnvironmentMapLight>>,
611 Has<RenderViewLightProbes<IrradianceVolume>>,
612 ),
613 Has<OrderIndependentTransparencySettings>,
614 )>,
615) where
616 M::Data: PartialEq + Eq + Hash + Clone,
617{
618 for (
619 view_entity,
620 view,
621 visible_entities,
622 msaa,
623 tonemapping,
624 dither,
625 shadow_filter_method,
626 ssao,
627 (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
628 camera_3d,
629 temporal_jitter,
630 projection,
631 (has_environment_maps, has_irradiance_volumes),
632 has_oit,
633 ) in &views
634 {
635 let (
636 Some(opaque_phase),
637 Some(alpha_mask_phase),
638 Some(transmissive_phase),
639 Some(transparent_phase),
640 ) = (
641 opaque_render_phases.get_mut(&view_entity),
642 alpha_mask_render_phases.get_mut(&view_entity),
643 transmissive_render_phases.get_mut(&view_entity),
644 transparent_render_phases.get_mut(&view_entity),
645 )
646 else {
647 continue;
648 };
649
650 let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
651 let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial<M>>();
652 let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial<M>>();
653 let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial<M>>();
654
655 let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
656 | MeshPipelineKey::from_hdr(view.hdr);
657
658 if normal_prepass {
659 view_key |= MeshPipelineKey::NORMAL_PREPASS;
660 }
661
662 if depth_prepass {
663 view_key |= MeshPipelineKey::DEPTH_PREPASS;
664 }
665
666 if motion_vector_prepass {
667 view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
668 }
669
670 if deferred_prepass {
671 view_key |= MeshPipelineKey::DEFERRED_PREPASS;
672 }
673
674 if temporal_jitter {
675 view_key |= MeshPipelineKey::TEMPORAL_JITTER;
676 }
677
678 if has_environment_maps {
679 view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
680 }
681
682 if has_irradiance_volumes {
683 view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
684 }
685
686 if has_oit {
687 view_key |= MeshPipelineKey::OIT_ENABLED;
688 }
689
690 if let Some(projection) = projection {
691 view_key |= match projection {
692 Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
693 Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
694 };
695 }
696
697 match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
698 ShadowFilteringMethod::Hardware2x2 => {
699 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
700 }
701 ShadowFilteringMethod::Gaussian => {
702 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
703 }
704 ShadowFilteringMethod::Temporal => {
705 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
706 }
707 }
708
709 if !view.hdr {
710 if let Some(tonemapping) = tonemapping {
711 view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
712 view_key |= tonemapping_pipeline_key(*tonemapping);
713 }
714 if let Some(DebandDither::Enabled) = dither {
715 view_key |= MeshPipelineKey::DEBAND_DITHER;
716 }
717 }
718 if ssao {
719 view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
720 }
721 if let Some(camera_3d) = camera_3d {
722 view_key |= screen_space_specular_transmission_pipeline_key(
723 camera_3d.screen_space_specular_transmission_quality,
724 );
725 }
726
727 let rangefinder = view.rangefinder3d();
728 for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh3d>>() {
729 let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
730 continue;
731 };
732 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
733 else {
734 continue;
735 };
736 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
737 continue;
738 };
739 let Some(material) = render_materials.get(*material_asset_id) else {
740 continue;
741 };
742
743 let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
744 mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
745 material.properties.alpha_mode,
746 msaa,
747 ));
748 let mut mesh_key = view_key
749 | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
750 | mesh_pipeline_key_bits;
751
752 let lightmap_image = render_lightmaps
753 .render_lightmaps
754 .get(visible_entity)
755 .map(|lightmap| lightmap.image);
756 if lightmap_image.is_some() {
757 mesh_key |= MeshPipelineKey::LIGHTMAPPED;
758 }
759
760 if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
761 mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
762 }
763
764 if motion_vector_prepass {
765 if mesh_instance
767 .flags
768 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
769 {
770 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
771 }
772 if mesh_instance
773 .flags
774 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
775 {
776 mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
777 }
778 }
779
780 let pipeline_id = pipelines.specialize(
781 &pipeline_cache,
782 &material_pipeline,
783 MaterialPipelineKey {
784 mesh_key,
785 bind_group_data: material.key.clone(),
786 },
787 &mesh.layout,
788 );
789 let pipeline_id = match pipeline_id {
790 Ok(id) => id,
791 Err(err) => {
792 error!("{}", err);
793 continue;
794 }
795 };
796
797 mesh_instance
798 .material_bind_group_id
799 .set(material.get_bind_group_id());
800
801 match mesh_key
802 .intersection(MeshPipelineKey::BLEND_RESERVED_BITS | MeshPipelineKey::MAY_DISCARD)
803 {
804 MeshPipelineKey::BLEND_OPAQUE | MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE => {
805 if material.properties.reads_view_transmission_texture {
806 let distance = rangefinder.distance_translation(&mesh_instance.translation)
807 + material.properties.depth_bias;
808 transmissive_phase.add(Transmissive3d {
809 entity: (*render_entity, *visible_entity),
810 draw_function: draw_transmissive_pbr,
811 pipeline: pipeline_id,
812 distance,
813 batch_range: 0..1,
814 extra_index: PhaseItemExtraIndex::NONE,
815 });
816 } else if material.properties.render_method == OpaqueRendererMethod::Forward {
817 let bin_key = Opaque3dBinKey {
818 draw_function: draw_opaque_pbr,
819 pipeline: pipeline_id,
820 asset_id: mesh_instance.mesh_asset_id.into(),
821 material_bind_group_id: material.get_bind_group_id().0,
822 lightmap_image,
823 };
824 opaque_phase.add(
825 bin_key,
826 (*render_entity, *visible_entity),
827 BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
828 );
829 }
830 }
831 MeshPipelineKey::MAY_DISCARD => {
833 if material.properties.reads_view_transmission_texture {
834 let distance = rangefinder.distance_translation(&mesh_instance.translation)
835 + material.properties.depth_bias;
836 transmissive_phase.add(Transmissive3d {
837 entity: (*render_entity, *visible_entity),
838 draw_function: draw_transmissive_pbr,
839 pipeline: pipeline_id,
840 distance,
841 batch_range: 0..1,
842 extra_index: PhaseItemExtraIndex::NONE,
843 });
844 } else if material.properties.render_method == OpaqueRendererMethod::Forward {
845 let bin_key = OpaqueNoLightmap3dBinKey {
846 draw_function: draw_alpha_mask_pbr,
847 pipeline: pipeline_id,
848 asset_id: mesh_instance.mesh_asset_id.into(),
849 material_bind_group_id: material.get_bind_group_id().0,
850 };
851 alpha_mask_phase.add(
852 bin_key,
853 (*render_entity, *visible_entity),
854 BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
855 );
856 }
857 }
858 _ => {
859 let distance = rangefinder.distance_translation(&mesh_instance.translation)
860 + material.properties.depth_bias;
861 transparent_phase.add(Transparent3d {
862 entity: (*render_entity, *visible_entity),
863 draw_function: draw_transparent_pbr,
864 pipeline: pipeline_id,
865 distance,
866 batch_range: 0..1,
867 extra_index: PhaseItemExtraIndex::NONE,
868 });
869 }
870 }
871 }
872 }
873}
874
875#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
877#[reflect(Resource, Default, Debug)]
878pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
879
880impl DefaultOpaqueRendererMethod {
881 pub fn forward() -> Self {
882 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
883 }
884
885 pub fn deferred() -> Self {
886 DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
887 }
888
889 pub fn set_to_forward(&mut self) {
890 self.0 = OpaqueRendererMethod::Forward;
891 }
892
893 pub fn set_to_deferred(&mut self) {
894 self.0 = OpaqueRendererMethod::Deferred;
895 }
896}
897
898#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
917pub enum OpaqueRendererMethod {
918 #[default]
919 Forward,
920 Deferred,
921 Auto,
922}
923
924pub struct MaterialProperties {
926 pub render_method: OpaqueRendererMethod,
929 pub alpha_mode: AlphaMode,
931 pub mesh_pipeline_key_bits: MeshPipelineKey,
936 pub depth_bias: f32,
940 pub reads_view_transmission_texture: bool,
945}
946
947pub struct PreparedMaterial<T: Material> {
949 pub bindings: Vec<(u32, OwnedBindingResource)>,
950 pub bind_group: BindGroup,
951 pub key: T::Data,
952 pub properties: MaterialProperties,
953}
954
955impl<M: Material> RenderAsset for PreparedMaterial<M> {
956 type SourceAsset = M;
957
958 type Param = (
959 SRes<RenderDevice>,
960 SRes<MaterialPipeline<M>>,
961 SRes<DefaultOpaqueRendererMethod>,
962 M::Param,
963 );
964
965 fn prepare_asset(
966 material: Self::SourceAsset,
967 (render_device, pipeline, default_opaque_render_method, ref mut material_param): &mut SystemParamItem<Self::Param>,
968 ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
969 match material.as_bind_group(&pipeline.material_layout, render_device, material_param) {
970 Ok(prepared) => {
971 let method = match material.opaque_render_method() {
972 OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
973 OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
974 OpaqueRendererMethod::Auto => default_opaque_render_method.0,
975 };
976 let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
977 mesh_pipeline_key_bits.set(
978 MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
979 material.reads_view_transmission_texture(),
980 );
981
982 Ok(PreparedMaterial {
983 bindings: prepared.bindings,
984 bind_group: prepared.bind_group,
985 key: prepared.data,
986 properties: MaterialProperties {
987 alpha_mode: material.alpha_mode(),
988 depth_bias: material.depth_bias(),
989 reads_view_transmission_texture: mesh_pipeline_key_bits
990 .contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
991 render_method: method,
992 mesh_pipeline_key_bits,
993 },
994 })
995 }
996 Err(AsBindGroupError::RetryNextUpdate) => {
997 Err(PrepareAssetError::RetryNextUpdate(material))
998 }
999 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1000 }
1001 }
1002}
1003
1004#[derive(Component, Clone, Copy, Default, PartialEq, Eq, Deref, DerefMut)]
1005pub struct MaterialBindGroupId(pub Option<BindGroupId>);
1006
1007impl MaterialBindGroupId {
1008 pub fn new(id: BindGroupId) -> Self {
1009 Self(Some(id))
1010 }
1011}
1012
1013impl From<BindGroup> for MaterialBindGroupId {
1014 fn from(value: BindGroup) -> Self {
1015 Self::new(value.id())
1016 }
1017}
1018
1019#[derive(Default)]
1022pub struct AtomicMaterialBindGroupId(AtomicU32);
1023
1024impl AtomicMaterialBindGroupId {
1025 pub fn set(&self, id: MaterialBindGroupId) {
1030 let id = if let Some(id) = id.0 {
1031 NonZero::<u32>::from(id).get()
1032 } else {
1033 0
1034 };
1035 self.0.store(id, Ordering::Relaxed);
1036 }
1037
1038 pub fn get(&self) -> MaterialBindGroupId {
1043 MaterialBindGroupId(
1044 NonZero::<u32>::new(self.0.load(Ordering::Relaxed)).map(BindGroupId::from),
1045 )
1046 }
1047}
1048
1049impl<T: Material> PreparedMaterial<T> {
1050 pub fn get_bind_group_id(&self) -> MaterialBindGroupId {
1051 MaterialBindGroupId(Some(self.bind_group.id()))
1052 }
1053}