diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d1c521397511e..e693b3b0ef56d 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -23,7 +23,7 @@ use bevy_render::{ SetItemPipeline, TrackedRenderPass, }, render_resource::{ - BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, + BindGroup, BindGroupLayout, Face, RenderPipelineCache, RenderPipelineDescriptor, Shader, SpecializedPipeline, SpecializedPipelines, }, renderer::RenderDevice, @@ -65,6 +65,12 @@ pub trait Material: Asset + RenderAsset { AlphaMode::Opaque } + /// Returns whether this material is double-sided. Defaults to [`false`]. + #[allow(unused_variables)] + fn double_sided(material: &::PreparedAsset) -> bool { + false + } + /// The dynamic uniform indices to set for the given `material`'s [`BindGroup`]. /// Defaults to an empty array / no dynamic uniform indices. #[allow(unused_variables)] @@ -98,6 +104,11 @@ impl SpecializedMaterial for M { ::alpha_mode(material) } + #[inline] + fn double_sided(material: &::PreparedAsset) -> bool { + ::double_sided(material) + } + #[inline] fn vertex_shader(asset_server: &AssetServer) -> Option> { ::vertex_shader(asset_server) @@ -158,6 +169,12 @@ pub trait SpecializedMaterial: Asset + RenderAsset { AlphaMode::Opaque } + /// Returns whether this material is double-sided. Defaults to [`false`]. + #[allow(unused_variables)] + fn double_sided(material: &::PreparedAsset) -> bool { + false + } + /// The dynamic uniform indices to set for the given `material`'s [`BindGroup`]. /// Defaults to an empty array / no dynamic uniform indices. #[allow(unused_variables)] @@ -326,6 +343,9 @@ pub fn queue_material_meshes( if let AlphaMode::Blend = alpha_mode { mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS } + if !M::double_sided(material) { + mesh_key |= MeshPipelineKey::from_cull_mode(Some(Face::Back)) + } let specialized_key = M::key(material); let pipeline_id = pipelines.specialize( diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 26ed132faf25f..06f8ce67811c0 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -153,6 +153,7 @@ pub struct GpuStandardMaterial { pub flags: StandardMaterialFlags, pub base_color_texture: Option>, pub alpha_mode: AlphaMode, + pub double_sided: bool, } impl RenderAsset for StandardMaterial { @@ -320,6 +321,7 @@ impl RenderAsset for StandardMaterial { has_normal_map, base_color_texture: material.base_color_texture, alpha_mode: material.alpha_mode, + double_sided: material.double_sided, }) } } @@ -477,4 +479,9 @@ impl SpecializedMaterial for StandardMaterial { fn alpha_mode(render_asset: &::PreparedAsset) -> AlphaMode { render_asset.alpha_mode } + + #[inline] + fn double_sided(render_asset: &::PreparedAsset) -> bool { + render_asset.double_sided + } } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3e8965aa28ee1..df64424366002 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -367,11 +367,12 @@ bitflags::bitflags! { // NOTE: Apparently quadro drivers support up to 64x MSAA. /// MSAA uses the highest 6 bits for the MSAA sample count - 1 to support up to 64x MSAA. pub struct MeshPipelineKey: u32 { - const NONE = 0; - const VERTEX_TANGENTS = (1 << 0); - const TRANSPARENT_MAIN_PASS = (1 << 1); - const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS; + const NONE = 0; + const VERTEX_TANGENTS = (1 << 0); + const TRANSPARENT_MAIN_PASS = (1 << 1); + const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS; const PRIMITIVE_TOPOLOGY_RESERVED_BITS = MeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << MeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; + const CULL_MODE_RESERVED_BITS = MeshPipelineKey::CULL_MODE_MASK_BITS << MeshPipelineKey::CULL_MODE_SHIFT_BITS; } } @@ -380,6 +381,8 @@ impl MeshPipelineKey { const MSAA_SHIFT_BITS: u32 = 32 - 6; const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111; const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = Self::MSAA_SHIFT_BITS - 3; + const CULL_MODE_MASK_BITS: u32 = 0b11; + const CULL_MODE_SHIFT_BITS: u32 = Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS - 2; pub fn from_msaa_samples(msaa_samples: u32) -> Self { let msaa_bits = ((msaa_samples - 1) & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS; @@ -409,6 +412,26 @@ impl MeshPipelineKey { _ => PrimitiveTopology::default(), } } + + pub fn from_cull_mode(cull_mode: Option) -> Self { + let cull_mode = match cull_mode { + None => 0, + Some(Face::Front) => 1, + Some(Face::Back) => 2, + }; + let cull_mode_bits = (cull_mode & Self::CULL_MODE_MASK_BITS) << Self::CULL_MODE_SHIFT_BITS; + MeshPipelineKey::from_bits(cull_mode_bits).unwrap() + } + + pub fn cull_mode(&self) -> Option { + let cull_mode_bits = (self.bits >> Self::CULL_MODE_SHIFT_BITS) & Self::CULL_MODE_MASK_BITS; + match cull_mode_bits { + 0 => None, + 1 => Some(Face::Front), + 2 => Some(Face::Back), + _ => Some(Face::Back), + } + } } impl SpecializedPipeline for MeshPipeline { @@ -519,7 +542,7 @@ impl SpecializedPipeline for MeshPipeline { layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]), primitive: PrimitiveState { front_face: FrontFace::Ccw, - cull_mode: Some(Face::Back), + cull_mode: key.cull_mode(), unclipped_depth: false, polygon_mode: PolygonMode::Fill, conservative: false,