bevy_light_2d 0.9.0

General purpose 2d lighting for the Bevy game engine.
Documentation
use bevy::core_pipeline::FullscreenShader;
use bevy::ecs::resource::Resource;
use bevy::ecs::world::{FromWorld, World};
use bevy::render::render_resource::binding_types::{sampler, texture_2d, uniform_buffer};
use bevy::render::render_resource::{
    BindGroupLayoutDescriptor, BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState,
    ColorWrites, FragmentState, GpuArrayBuffer, MultisampleState, PipelineCache, PrimitiveState,
    RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages,
    TextureFormat, TextureSampleType,
};
use bevy::render::renderer::RenderDevice;
use bevy::render::view::ViewUniform;

use crate::render::extract::{
    ExtractedAmbientLight2d, ExtractedPointLight2d, ExtractedSpotLight2d,
};

use super::{LIGHT_MAP_SHADER, PointLightMeta, SpotLightMeta};

const LIGHT_MAP_BIND_GROUP_LAYOUT: &str = "light_map_group_layout";
const LIGHT_MAP_PIPELINE: &str = "light_map_pipeline";

#[derive(Resource)]
pub struct LightMapPipeline {
    pub layout_descriptor: BindGroupLayoutDescriptor,
    pub sdf_sampler: Sampler,
    pub pipeline_id: CachedRenderPipelineId,
}

impl FromWorld for LightMapPipeline {
    fn from_world(world: &mut World) -> Self {
        let render_device = world.resource::<RenderDevice>();
        let limits = &render_device.limits();
        let layout_descriptor = BindGroupLayoutDescriptor::new(
            LIGHT_MAP_BIND_GROUP_LAYOUT,
            &BindGroupLayoutEntries::sequential(
                ShaderStages::FRAGMENT,
                (
                    uniform_buffer::<ViewUniform>(true),
                    uniform_buffer::<ExtractedAmbientLight2d>(true),
                    GpuArrayBuffer::<ExtractedPointLight2d>::binding_layout(limits),
                    uniform_buffer::<PointLightMeta>(false),
                    texture_2d(TextureSampleType::Float { filterable: true }),
                    sampler(SamplerBindingType::Filtering),
                    GpuArrayBuffer::<ExtractedSpotLight2d>::binding_layout(limits),
                    uniform_buffer::<SpotLightMeta>(false),
                ),
            ),
        );

        let sdf_sampler = render_device.create_sampler(&SamplerDescriptor::default());
        let fullscreen_shader = world.resource::<FullscreenShader>().clone();
        let pipeline_id =
            world
                .resource_mut::<PipelineCache>()
                .queue_render_pipeline(RenderPipelineDescriptor {
                    label: Some(LIGHT_MAP_PIPELINE.into()),
                    layout: vec![layout_descriptor.clone()],
                    vertex: fullscreen_shader.to_vertex_state(),
                    fragment: Some(FragmentState {
                        shader: LIGHT_MAP_SHADER,
                        shader_defs: vec![],
                        entry_point: Some("fragment".into()),
                        targets: vec![Some(ColorTargetState {
                            format: TextureFormat::Rgba16Float,
                            blend: None,
                            write_mask: ColorWrites::ALL,
                        })],
                    }),
                    primitive: PrimitiveState::default(),
                    depth_stencil: None,
                    multisample: MultisampleState::default(),
                    push_constant_ranges: vec![],
                    zero_initialize_workgroup_memory: false,
                });

        Self {
            layout_descriptor,
            sdf_sampler,
            pipeline_id,
        }
    }
}