veloren/voxygen/src/audio/ambient.rs

248 lines
8.9 KiB
Rust
Raw Normal View History

2020-11-14 00:27:09 +00:00
//! Handles ambient non-positional sounds
use crate::{
2022-06-04 01:25:17 +00:00
audio::{channel::AmbientChannelTag, AudioFrontend},
2020-11-14 00:27:09 +00:00
scene::Camera,
};
2020-11-07 01:55:59 +00:00
use client::Client;
2020-12-13 01:09:57 +00:00
use common::{
assets::{self, AssetExt, AssetHandle},
vol::ReadVol,
};
2021-04-06 15:47:03 +00:00
use common_state::State;
2020-11-07 01:55:59 +00:00
use serde::Deserialize;
use std::time::Instant;
2022-03-15 18:04:21 +00:00
use strum::IntoEnumIterator;
2020-11-07 01:55:59 +00:00
use tracing::warn;
2020-11-17 00:38:48 +00:00
use vek::*;
2020-11-07 01:55:59 +00:00
#[derive(Debug, Default, Deserialize)]
pub struct AmbientCollection {
2020-11-14 00:27:09 +00:00
tracks: Vec<AmbientItem>,
2020-11-07 01:55:59 +00:00
}
#[derive(Debug, Deserialize)]
2020-11-14 00:27:09 +00:00
pub struct AmbientItem {
2020-11-07 01:55:59 +00:00
path: String,
/// Length of the track in seconds
length: f32,
2020-11-17 00:38:48 +00:00
/// Specifies which ambient channel to play on
2020-11-14 00:27:09 +00:00
tag: AmbientChannelTag,
2020-11-07 01:55:59 +00:00
}
pub struct AmbientMgr {
pub ambience: AssetHandle<AmbientCollection>,
2020-11-07 01:55:59 +00:00
}
impl AmbientMgr {
2020-11-07 10:02:29 +00:00
pub fn maintain(
&mut self,
audio: &mut AudioFrontend,
state: &State,
client: &Client,
camera: &Camera,
) {
2022-06-04 01:25:17 +00:00
// Checks if the ambience volume is set to zero or audio is disabled
// This prevents us from running all the following code unnecessarily
if !audio.ambience_enabled() {
return;
}
2022-03-23 06:50:38 +00:00
let ambience_volume = audio.get_ambience_volume();
2022-06-04 01:25:17 +00:00
let ambience = self.ambience.read();
// Iterate through each tag
for tag in AmbientChannelTag::iter() {
// If the conditions warrant creating a channel of that tag
2022-06-04 01:25:17 +00:00
if AmbientChannelTag::get_tag_volume(tag, client, camera)
> match tag {
AmbientChannelTag::Wind => 0.0,
AmbientChannelTag::Rain => 0.1,
AmbientChannelTag::Thunder => 0.0,
AmbientChannelTag::Leaves => 0.1,
}
&& audio.get_ambient_channel(tag).is_none()
{
2022-06-04 01:25:17 +00:00
audio.new_ambient_channel(tag);
}
// If a channel exists run volume code
if let Some(channel_index) = audio.get_ambient_channel_index(tag) {
let channel = &mut audio.ambient_channels[channel_index];
// Maintain: get the correct multiplier of whatever the tag of the current
// channel is
let target_volume = get_target_volume(tag, state, client, camera);
// Get multiplier of the current channel
let initial_volume = channel.multiplier;
// Lerp multiplier of current channel
// TODO: Make this not framerate dependent
channel.multiplier = Lerp::lerp(initial_volume, target_volume, 0.02);
// Update with sfx volume
channel.set_volume(ambience_volume);
// Set the duration of the loop to whatever the current value is (0.0 by
// default)
// If the sound should loop at this point:
if channel.began_playing.elapsed().as_secs_f32() > channel.next_track_change {
let track = ambience.tracks.iter().find(|track| track.tag == tag);
// Set the channel's start point at this instant
channel.began_playing = Instant::now();
if let Some(track) = track {
// Set loop duration to the one specified in the ron
channel.next_track_change = track.length;
// Play the file of the current tag at the current multiplier;
let current_multiplier = channel.multiplier;
audio.play_ambient(tag, &track.path, current_multiplier);
2022-03-19 09:37:45 +00:00
}
2022-06-04 01:25:17 +00:00
};
2022-06-04 01:25:17 +00:00
// Remove channel if not playing
if audio.ambient_channels[channel_index].multiplier <= 0.001 {
audio.ambient_channels.remove(channel_index);
};
2021-11-20 01:46:14 +00:00
}
}
}
}
2022-05-06 19:20:36 +00:00
impl AmbientChannelTag {
// Gets appropriate volume for each tag
2022-05-06 19:20:36 +00:00
pub fn get_tag_volume(tag: AmbientChannelTag, client: &Client, camera: &Camera) -> f32 {
match tag {
AmbientChannelTag::Wind => {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
} else {
(0.0, 0.0)
};
// Wind volume increases with altitude
let alt_multiplier = (cam_pos.z / 1200.0).abs();
// Tree density factors into wind volume. The more trees,
// the lower wind volume. The trees make more of an impact
// the closer the camera is to the ground.
let tree_multiplier = ((1.0 - tree_density)
+ ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))
.min(1.0);
// Lastly, we of course have to take into account actual wind speed from
// weathersim
// Client wind speed is a float approx. -30.0 to 30.0 (polarity depending on
// direction)
let wind_speed_multiplier = (client.weather_at_player().wind.magnitude_squared()
/ 30.0_f32.powi(2))
.min(1.0);
alt_multiplier
* tree_multiplier
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))
.min(1.0)
},
AmbientChannelTag::Rain => {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
} else {
0.0
};
// Make rain diminish with camera distance above terrain
let camera_multiplier =
1.0 - ((cam_pos.z - terrain_alt).abs() / 75.0).powi(2).min(1.0);
let rain_intensity = (client.weather_at_player().rain * 500.0) * camera_multiplier;
rain_intensity.min(0.9)
},
AmbientChannelTag::Thunder => {
2022-06-04 01:25:17 +00:00
let rain_intensity = client.weather_at_player().rain * 500.0;
if rain_intensity < 0.7 {
0.0
} else {
2022-06-04 01:25:17 +00:00
rain_intensity
}
},
AmbientChannelTag::Leaves => {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
} else {
(0.0, 0.0)
};
// Tree density factors into leaves volume. The more trees,
// the higher volume. The trees make more of an impact
// the closer the camera is to the ground
let tree_multiplier = 1.0
- (((1.0 - tree_density)
+ ((cam_pos.z - terrain_alt + 20.0).abs() / 150.0).powi(2))
.min(1.0));
if tree_multiplier > 0.1 {
tree_multiplier
} else {
0.0
}
},
2022-03-20 07:07:23 +00:00
}
}
2021-11-20 01:46:14 +00:00
}
2020-11-07 01:55:59 +00:00
2022-06-04 01:25:17 +00:00
/// Checks various factors to determine the target volume to lerp to
fn get_target_volume(
tag: AmbientChannelTag,
state: &State,
client: &Client,
camera: &Camera,
) -> f32 {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let mut volume_multiplier: f32 = AmbientChannelTag::get_tag_volume(tag, client, camera);
let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
} else {
0.0
};
// Checks if the camera is underwater to diminish ambient sounds
if state
.terrain()
.get((cam_pos).map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false)
{
volume_multiplier *= 0.1;
2022-05-06 19:20:36 +00:00
}
2022-06-04 01:25:17 +00:00
// Is the camera roughly under the terrain?
if cam_pos.z < terrain_alt - 20.0 {
volume_multiplier = 0.0;
2022-05-06 19:20:36 +00:00
}
2022-06-04 01:25:17 +00:00
volume_multiplier.clamped(0.0, 1.0)
2022-05-06 19:20:36 +00:00
}
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {
2021-11-20 01:46:14 +00:00
AmbientCollection::load_or_insert_with("voxygen.audio.ambient", |error| {
warn!(
"Error reading ambience config file, ambience will not be available: {:#?}",
error
);
AmbientCollection::default()
})
2020-11-07 01:55:59 +00:00
}
2020-12-12 22:14:24 +00:00
impl assets::Asset for AmbientCollection {
type Loader = assets::RonLoader;
2020-12-13 01:09:57 +00:00
const EXTENSION: &'static str = "ron";
}