1use bevy_app::{App, Plugin};
2use bevy_asset::{load_internal_asset, Handle};
3use bevy_color::{ColorToComponents, LinearRgba};
4use bevy_ecs::prelude::*;
5use bevy_math::{Vec3, Vec4};
6use bevy_render::{
7 extract_component::ExtractComponentPlugin,
8 render_resource::{DynamicUniformBuffer, Shader, ShaderType},
9 renderer::{RenderDevice, RenderQueue},
10 view::ExtractedView,
11 Render, RenderApp, RenderSet,
12};
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 const FOG_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(4913569193382610166);
131
132pub struct FogPlugin;
134
135impl Plugin for FogPlugin {
136 fn build(&self, app: &mut App) {
137 load_internal_asset!(app, FOG_SHADER_HANDLE, "fog.wgsl", Shader::from_wgsl);
138
139 app.register_type::<DistanceFog>();
140 app.add_plugins(ExtractComponentPlugin::<DistanceFog>::default());
141
142 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
143 render_app
144 .init_resource::<FogMeta>()
145 .add_systems(Render, prepare_fog.in_set(RenderSet::PrepareResources));
146 }
147 }
148}