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