use bevy_asset::{load_embedded_asset, AssetServer, Handle};
use bevy_ecs::{
component::Component,
entity::Entity,
query::{Has, With},
resource::Resource,
system::{Commands, Query, Res, ResMut},
};
use bevy_render::{
render_resource::{
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayoutDescriptor,
BindGroupLayoutEntries, CachedRenderPipelineId, CompareFunction, DepthStencilState,
FragmentState, MultisampleState, PipelineCache, RenderPipelineDescriptor, ShaderStages,
SpecializedRenderPipeline, SpecializedRenderPipelines,
},
renderer::RenderDevice,
view::{Msaa, ViewUniform, ViewUniforms},
};
use bevy_shader::Shader;
use bevy_utils::prelude::default;
use crate::{
core_3d::CORE_3D_DEPTH_FORMAT,
prepass::{
prepass_target_descriptors, MotionVectorPrepass, NormalPrepass, PreviousViewData,
PreviousViewUniforms,
},
FullscreenShader, Skybox,
};
#[derive(Resource)]
pub struct SkyboxPrepassPipeline {
bind_group_layout: BindGroupLayoutDescriptor,
fullscreen_shader: FullscreenShader,
fragment_shader: Handle<Shader>,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct SkyboxPrepassPipelineKey {
samples: u32,
normal_prepass: bool,
}
#[derive(Component)]
pub struct RenderSkyboxPrepassPipeline(pub CachedRenderPipelineId);
#[derive(Component)]
pub struct SkyboxPrepassBindGroup(pub BindGroup);
pub fn init_skybox_prepass_pipeline(
mut commands: Commands,
fullscreen_shader: Res<FullscreenShader>,
asset_server: Res<AssetServer>,
) {
commands.insert_resource(SkyboxPrepassPipeline {
bind_group_layout: BindGroupLayoutDescriptor::new(
"skybox_prepass_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
uniform_buffer::<ViewUniform>(true),
uniform_buffer::<PreviousViewData>(true),
),
),
),
fullscreen_shader: fullscreen_shader.clone(),
fragment_shader: load_embedded_asset!(asset_server.as_ref(), "skybox_prepass.wgsl"),
});
}
impl SpecializedRenderPipeline for SkyboxPrepassPipeline {
type Key = SkyboxPrepassPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
RenderPipelineDescriptor {
label: Some("skybox_prepass_pipeline".into()),
layout: vec![self.bind_group_layout.clone()],
vertex: self.fullscreen_shader.to_vertex_state(),
depth_stencil: Some(DepthStencilState {
format: CORE_3D_DEPTH_FORMAT,
depth_write_enabled: false,
depth_compare: CompareFunction::GreaterEqual,
stencil: default(),
bias: default(),
}),
multisample: MultisampleState {
count: key.samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(FragmentState {
shader: self.fragment_shader.clone(),
targets: prepass_target_descriptors(key.normal_prepass, true, false),
..default()
}),
..default()
}
}
}
pub fn prepare_skybox_prepass_pipelines(
mut commands: Commands,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPrepassPipeline>>,
pipeline: Res<SkyboxPrepassPipeline>,
views: Query<(Entity, Has<NormalPrepass>, &Msaa), (With<Skybox>, With<MotionVectorPrepass>)>,
) {
for (entity, normal_prepass, msaa) in &views {
let pipeline_key = SkyboxPrepassPipelineKey {
samples: msaa.samples(),
normal_prepass,
};
let render_skybox_prepass_pipeline =
pipelines.specialize(&pipeline_cache, &pipeline, pipeline_key);
commands
.entity(entity)
.insert(RenderSkyboxPrepassPipeline(render_skybox_prepass_pipeline));
}
}
pub fn prepare_skybox_prepass_bind_groups(
mut commands: Commands,
pipeline: Res<SkyboxPrepassPipeline>,
view_uniforms: Res<ViewUniforms>,
prev_view_uniforms: Res<PreviousViewUniforms>,
render_device: Res<RenderDevice>,
pipeline_cache: Res<PipelineCache>,
views: Query<Entity, (With<Skybox>, With<MotionVectorPrepass>)>,
) {
for entity in &views {
let (Some(view_uniforms), Some(prev_view_uniforms)) = (
view_uniforms.uniforms.binding(),
prev_view_uniforms.uniforms.binding(),
) else {
continue;
};
let bind_group = render_device.create_bind_group(
"skybox_prepass_bind_group",
&pipeline_cache.get_bind_group_layout(&pipeline.bind_group_layout),
&BindGroupEntries::sequential((view_uniforms, prev_view_uniforms)),
);
commands
.entity(entity)
.insert(SkyboxPrepassBindGroup(bind_group));
}
}