bevy_exponential_height_fog 0.1.0

Standalone exponential height fog extension for Bevy volumetric fog.
Documentation
use bevy::prelude::*;
use bevy::render::diagnostic::RecordDiagnostics;
use bevy::render::extract_component::{ComponentUniforms, DynamicUniformIndex};
use bevy::render::render_graph::{NodeRunError, RenderGraphContext, ViewNode};
use bevy::render::render_resource::{
    BindGroupEntries, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
};
use bevy::render::renderer::RenderContext;
use bevy::render::view::{Msaa, ViewDepthTexture, ViewTarget, ViewUniformOffset, ViewUniforms};

use crate::component::ExponentialHeightFogUniform;
use crate::pipeline::{ExponentialHeightFogPipeline, ExponentialHeightFogPipelineId};

#[derive(Default)]
pub struct ExponentialHeightFogNode;

impl ViewNode for ExponentialHeightFogNode {
    type ViewQuery = (
        &'static ViewTarget,
        &'static ViewDepthTexture,
        &'static ExponentialHeightFogPipelineId,
        &'static ViewUniformOffset,
        &'static DynamicUniformIndex<ExponentialHeightFogUniform>,
        &'static Msaa,
    );

    fn run(
        &self,
        _graph: &mut RenderGraphContext,
        render_context: &mut RenderContext,
        (
            view_target,
            view_depth_texture,
            pipeline_id,
            view_uniform_offset,
            fog_uniform_index,
            msaa,
        ): bevy::ecs::query::QueryItem<'_, '_, Self::ViewQuery>,
        world: &World,
    ) -> Result<(), NodeRunError> {
        let pipeline_cache = world.resource::<PipelineCache>();
        let fog_pipeline = world.resource::<ExponentialHeightFogPipeline>();
        let view_uniforms = world.resource::<ViewUniforms>();
        let fog_uniforms = world.resource::<ComponentUniforms<ExponentialHeightFogUniform>>();

        let Some(pipeline) = pipeline_cache.get_render_pipeline(pipeline_id.0) else {
            return Ok(());
        };
        let Some(view_uniforms_binding) = view_uniforms.uniforms.binding() else {
            return Ok(());
        };
        let Some(fog_uniforms_binding) = fog_uniforms.uniforms().binding() else {
            return Ok(());
        };

        let diagnostics = render_context.diagnostic_recorder();
        let post_process = view_target.post_process_write();
        let bind_group_layout = if msaa.samples() > 1 {
            &fog_pipeline.layout_msaa
        } else {
            &fog_pipeline.layout
        };
        let bind_group = render_context.render_device().create_bind_group(
            Some("exponential_height_fog_bind_group"),
            &pipeline_cache.get_bind_group_layout(bind_group_layout),
            &BindGroupEntries::sequential((
                view_uniforms_binding,
                view_depth_texture.view(),
                post_process.source,
                &fog_pipeline.sampler,
                fog_uniforms_binding,
            )),
        );

        let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
            label: Some("exponential_height_fog"),
            color_attachments: &[Some(RenderPassColorAttachment {
                view: post_process.destination,
                depth_slice: None,
                resolve_target: None,
                ops: Operations::default(),
            })],
            depth_stencil_attachment: None,
            timestamp_writes: None,
            occlusion_query_set: None,
        });
        let pass_span = diagnostics.pass_span(&mut render_pass, "exponential_height_fog");

        render_pass.set_render_pipeline(pipeline);
        render_pass.set_bind_group(
            0,
            &bind_group,
            &[view_uniform_offset.offset, fog_uniform_index.index()],
        );
        render_pass.draw(0..3, 0..1);

        pass_span.end(&mut render_pass);

        Ok(())
    }
}