veloren_voxygen/audio/
ambience.rs1use crate::{
3 audio::{AudioFrontend, channel::AmbienceChannelTag},
4 scene::Camera,
5 settings::AudioSettings,
6};
7use client::Client;
8use common::{
9 assets::{AssetExt, AssetHandle, Ron},
10 terrain::site::SiteKindMeta,
11 vol::ReadVol,
12};
13use common_state::State;
14use serde::Deserialize;
15use strum::IntoEnumIterator;
16use tracing::warn;
17use vek::*;
18
19#[derive(Debug, Default, Deserialize)]
20pub struct AmbienceCollection {
21 tracks: Vec<AmbienceItem>,
22}
23
24#[derive(Debug, Deserialize)]
25pub struct AmbienceItem {
26 path: String,
27 tag: AmbienceChannelTag,
29 start: usize,
30 end: usize,
31}
32
33pub struct AmbienceMgr {
34 pub ambience: AssetHandle<Ron<AmbienceCollection>>,
35}
36
37impl AmbienceMgr {
38 pub fn maintain(
39 &mut self,
40 audio: &mut AudioFrontend,
41 audio_settings: &AudioSettings,
42 state: &State,
43 client: &Client,
44 camera: &Camera,
45 ) {
46 if !audio.ambience_enabled() {
47 return;
48 }
49
50 let ambience_sounds = self.ambience.read();
51
52 let cam_pos = camera.get_pos_with_focus();
53
54 if state
56 .terrain()
57 .get(cam_pos.map(|e| e.floor() as i32))
58 .map(|b| b.is_liquid())
59 .unwrap_or(false)
60 {
61 audio.set_ambience_master_filter(888);
62 } else {
63 audio.set_ambience_master_filter(20000);
64 }
65
66 for tag in AmbienceChannelTag::iter() {
69 if let Some(inner) = audio.inner.as_mut()
72 && inner.channels.get_ambience_channel(tag).is_none()
73 {
74 inner.new_ambience_channel(tag);
75 let track = ambience_sounds
76 .0
77 .tracks
78 .iter()
79 .find(|track| track.tag == tag);
80 if let Some(track) = track {
81 audio.play_ambience_looping(tag, &track.path, track.start, track.end);
82 }
83 }
84 if let Some(inner) = audio.inner.as_mut()
85 && let Some(channel) = inner.channels.get_ambience_channel(tag)
86 {
87 let target_volume =
90 if !audio_settings.rain_ambience_enabled && tag == AmbienceChannelTag::Rain {
91 0.0
92 } else {
93 get_target_volume(tag, client, camera)
94 };
95
96 channel.fade_to(target_volume, 1.0);
98 }
99 }
100 }
101}
102
103impl AmbienceChannelTag {
104 pub fn tag_max_volume(tag: AmbienceChannelTag) -> f32 {
105 match tag {
106 AmbienceChannelTag::Wind => 1.0,
107 AmbienceChannelTag::Rain => 0.95,
108 AmbienceChannelTag::ThunderRumbling => 1.33,
109 AmbienceChannelTag::Leaves => 1.33,
110 AmbienceChannelTag::Cave => 1.0,
111 _ => 1.0,
112 }
113 }
114
115 pub fn get_tag_volume(tag: AmbienceChannelTag, client: &Client, camera: &Camera) -> f32 {
117 match tag {
118 AmbienceChannelTag::Wind => {
119 let focus_off = camera.get_focus_pos().map(f32::trunc);
120 let cam_pos = camera.dependents().cam_pos + focus_off;
121
122 let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
123 (chunk.meta().alt(), chunk.meta().tree_density())
124 } else {
125 (0.0, 0.0)
126 };
127
128 let alt_factor = (cam_pos.z / 1200.0).abs();
130
131 let tree_factor = ((1.0 - (tree_density * 0.5))
135 + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))
136 .min(1.0);
137
138 let wind_speed_factor = (client.weather_at_player().wind.magnitude_squared()
143 / 15.0_f32.powi(2))
144 .min(1.33);
145
146 (alt_factor
147 * tree_factor
148 * (wind_speed_factor + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)))
149 + (alt_factor * 0.15) * tree_factor
150 },
151 AmbienceChannelTag::Rain => {
152 let focus_off = camera.get_focus_pos().map(f32::trunc);
153 let cam_pos = camera.dependents().cam_pos + focus_off;
154
155 let terrain_alt = if let Some(chunk) = client.current_chunk() {
156 chunk.meta().alt()
157 } else {
158 0.0
159 };
160 let camera_factor = 1.0 - ((cam_pos.z - terrain_alt).abs() / 75.0).powi(2).min(1.0);
162
163 (client.weather_at_player().rain * 3.0) * camera_factor
164 },
165 AmbienceChannelTag::ThunderRumbling => {
166 let rain_intensity = client.weather_at_player().rain * 3.0;
167
168 if rain_intensity < 0.7 {
169 0.0
170 } else {
171 rain_intensity
172 }
173 },
174 AmbienceChannelTag::Leaves => {
175 let focus_off = camera.get_focus_pos().map(f32::trunc);
176 let cam_pos = camera.dependents().cam_pos + focus_off;
177
178 let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
179 (chunk.meta().alt(), chunk.meta().tree_density())
180 } else {
181 (0.0, 0.0)
182 };
183
184 let tree_factor = 1.0
188 - (((1.0 - tree_density)
189 + ((cam_pos.z - terrain_alt - 20.0).abs() / 150.0).powi(2))
190 .min(1.1));
191
192 let wind_speed_factor = (client.weather_at_player().wind.magnitude_squared()
194 / 20.0_f32.powi(2))
195 .min(1.0);
196
197 if tree_factor > 0.1 {
198 tree_factor * (1.0 + wind_speed_factor)
199 } else {
200 0.0
201 }
202 },
203 AmbienceChannelTag::Cave => {
204 let focus_off = camera.get_focus_pos().map(f32::trunc);
205 let cam_pos = camera.dependents().cam_pos + focus_off;
206
207 let terrain_alt = if let Some(chunk) = client.current_chunk() {
208 chunk.meta().alt()
209 } else {
210 0.0
211 };
212
213 let camera_factor = (-(cam_pos.z - terrain_alt) / 100.0).max(0.0);
215
216 if client.current_site() == SiteKindMeta::Cave {
217 camera_factor
218 } else {
219 0.0
220 }
221 },
222 _ => 1.0,
223 }
224 }
225}
226
227fn get_target_volume(tag: AmbienceChannelTag, client: &Client, camera: &Camera) -> f32 {
229 let focus_off = camera.get_focus_pos().map(f32::trunc);
230 let cam_pos = camera.dependents().cam_pos + focus_off;
231
232 let volume: f32 = AmbienceChannelTag::get_tag_volume(tag, client, camera);
233
234 let terrain_alt = if let Some(chunk) = client.current_chunk() {
235 chunk.meta().alt()
236 } else {
237 0.0
238 };
239
240 if tag != AmbienceChannelTag::Cave {
243 (volume * ((cam_pos.z - terrain_alt) / 50.0 + 1.0).clamped(0.0, 1.0))
244 .min(AmbienceChannelTag::tag_max_volume(tag))
245 } else {
246 volume.min(AmbienceChannelTag::tag_max_volume(tag))
247 }
248}
249
250pub fn load_ambience_items() -> AssetHandle<Ron<AmbienceCollection>> {
251 Ron::load_or_insert_with("voxygen.audio.ambience", |error| {
252 warn!(
253 "Error reading ambience config file, ambience will not be available: {:#?}",
254 error
255 );
256 Ron(AmbienceCollection::default())
257 })
258}