1use bevy_app::{App, Plugin};
2use bevy_color::{ColorToComponents, LinearRgba};
3use bevy_ecs::prelude::*;
4use bevy_math::{Vec3, Vec4};
5use bevy_render::{
6 extract_component::ExtractComponentPlugin,
7 render_resource::{DynamicUniformBuffer, ShaderType},
8 renderer::{RenderDevice, RenderQueue},
9 view::ExtractedView,
10 Render, RenderApp, RenderSystems,
11};
12use bevy_shader::load_shader_library;
13
14use crate::{DistanceFog, FogFalloff};
15
16#[derive(Copy, Clone, ShaderType, Default, Debug)]
18pub struct GpuFog {
19 base_color: Vec4,
21 directional_light_color: Vec4,
23 be: Vec3,
26 directional_light_exponent: f32,
28 bi: Vec3,
31 mode: u32,
33}
34
35const GPU_FOG_MODE_OFF: u32 = 0;
37const GPU_FOG_MODE_LINEAR: u32 = 1;
38const GPU_FOG_MODE_EXPONENTIAL: u32 = 2;
39const GPU_FOG_MODE_EXPONENTIAL_SQUARED: u32 = 3;
40const GPU_FOG_MODE_ATMOSPHERIC: u32 = 4;
41
42#[derive(Default, Resource)]
44pub struct FogMeta {
45 pub gpu_fogs: DynamicUniformBuffer<GpuFog>,
46}
47
48pub fn prepare_fog(
50 mut commands: Commands,
51 render_device: Res<RenderDevice>,
52 render_queue: Res<RenderQueue>,
53 mut fog_meta: ResMut<FogMeta>,
54 views: Query<(Entity, Option<&DistanceFog>), With<ExtractedView>>,
55) {
56 let views_iter = views.iter();
57 let view_count = views_iter.len();
58 let Some(mut writer) = fog_meta
59 .gpu_fogs
60 .get_writer(view_count, &render_device, &render_queue)
61 else {
62 return;
63 };
64 for (entity, fog) in views_iter {
65 let gpu_fog = if let Some(fog) = fog {
66 match &fog.falloff {
67 FogFalloff::Linear { start, end } => GpuFog {
68 mode: GPU_FOG_MODE_LINEAR,
69 base_color: LinearRgba::from(fog.color).to_vec4(),
70 directional_light_color: LinearRgba::from(fog.directional_light_color)
71 .to_vec4(),
72 directional_light_exponent: fog.directional_light_exponent,
73 be: Vec3::new(*start, *end, 0.0),
74 ..Default::default()
75 },
76 FogFalloff::Exponential { density } => GpuFog {
77 mode: GPU_FOG_MODE_EXPONENTIAL,
78 base_color: LinearRgba::from(fog.color).to_vec4(),
79 directional_light_color: LinearRgba::from(fog.directional_light_color)
80 .to_vec4(),
81 directional_light_exponent: fog.directional_light_exponent,
82 be: Vec3::new(*density, 0.0, 0.0),
83 ..Default::default()
84 },
85 FogFalloff::ExponentialSquared { density } => GpuFog {
86 mode: GPU_FOG_MODE_EXPONENTIAL_SQUARED,
87 base_color: LinearRgba::from(fog.color).to_vec4(),
88 directional_light_color: LinearRgba::from(fog.directional_light_color)
89 .to_vec4(),
90 directional_light_exponent: fog.directional_light_exponent,
91 be: Vec3::new(*density, 0.0, 0.0),
92 ..Default::default()
93 },
94 FogFalloff::Atmospheric {
95 extinction,
96 inscattering,
97 } => GpuFog {
98 mode: GPU_FOG_MODE_ATMOSPHERIC,
99 base_color: LinearRgba::from(fog.color).to_vec4(),
100 directional_light_color: LinearRgba::from(fog.directional_light_color)
101 .to_vec4(),
102 directional_light_exponent: fog.directional_light_exponent,
103 be: *extinction,
104 bi: *inscattering,
105 },
106 }
107 } else {
108 GpuFog {
110 mode: GPU_FOG_MODE_OFF,
111 ..Default::default()
112 }
113 };
114
115 commands.entity(entity).insert(ViewFogUniformOffset {
117 offset: writer.write(&gpu_fog),
118 });
119 }
120}
121
122#[derive(Component)]
125pub struct ViewFogUniformOffset {
126 pub offset: u32,
127}
128
129pub struct FogPlugin;
131
132impl Plugin for FogPlugin {
133 fn build(&self, app: &mut App) {
134 load_shader_library!(app, "fog.wgsl");
135
136 app.add_plugins(ExtractComponentPlugin::<DistanceFog>::default());
137
138 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
139 render_app
140 .init_resource::<FogMeta>()
141 .add_systems(Render, prepare_fog.in_set(RenderSystems::PrepareResources));
142 }
143 }
144}