veloren/voxygen/src/audio/ambient.rs

349 lines
13 KiB
Rust
Raw Normal View History

2020-11-14 00:27:09 +00:00
//! Handles ambient non-positional sounds
use crate::{
audio::{
channel::{AmbientChannel, 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
}
/// Configuration for a single music track in the soundtrack
#[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-03-23 06:50:38 +00:00
let ambience_volume = audio.get_ambience_volume();
// iterate through each tag
for tag in AmbientChannelTag::iter() {
2022-03-19 09:37:45 +00:00
// check if current conditions necessitate the current tag at all
let should_create: bool = match tag {
AmbientChannelTag::Wind => self.check_wind_necessity(client, camera),
AmbientChannelTag::Rain => self.check_rain_necessity(client, camera),
2022-03-20 07:07:23 +00:00
AmbientChannelTag::Thunder => self.check_thunder_necessity(client),
AmbientChannelTag::Leaves => self.check_leaves_necessity(client, camera),
2022-03-19 09:37:45 +00:00
};
2022-03-20 07:07:23 +00:00
// if the conditions warrant creating a channel of that tag
if should_create && audio.get_ambient_channel(tag).is_none() {
2022-03-19 09:37:45 +00:00
// iterate through the supposed number of channels - one for each tag
for index in 0..AmbientChannelTag::iter().len() {
// if index would exceed current number of channels, create a new one with
// current tag
if index >= audio.ambient_channels.len() {
audio.new_ambient_channel(tag);
2022-03-15 18:04:21 +00:00
break;
2022-03-19 09:37:45 +00:00
}
2022-03-20 07:07:23 +00:00
}
2022-03-15 18:04:21 +00:00
// even if the conditions don't warrant the creation of a
// channel with that tag, but a channel with
// that tag remains nonetheless, run the code
2022-03-20 07:07:23 +00:00
} else if audio.get_ambient_channel(tag).is_some() {
for index in 0..AmbientChannelTag::iter().len() {
2022-03-19 09:37:45 +00:00
// update with sfx volume
2022-03-23 06:50:38 +00:00
audio.ambient_channels[index].set_volume(ambience_volume);
2022-03-19 09:37:45 +00:00
// if current channel's tag is not the current tag, move on to next channel
if audio.ambient_channels[index].get_tag() == tag {
// maintain: get the correct multiplier of whatever the tag of the current
// channel is
let target_volume =
audio.ambient_channels[index].maintain(state, client, camera);
// get multiplier of the current channel
let initial_volume = audio.ambient_channels[index].get_multiplier();
2022-03-19 09:37:45 +00:00
// lerp multiplier of current channel
audio.ambient_channels[index].set_multiplier(Lerp::lerp(
initial_volume,
target_volume,
0.01,
));
2022-03-19 09:37:45 +00:00
// set the duration of the loop to whatever the current value is (0.0 by
// default)
let next_track_change =
audio.ambient_channels[index].get_next_track_change();
2022-03-19 09:37:45 +00:00
// if the sound should loop at this point:
if audio.ambient_channels[index]
.get_began_playing()
.elapsed()
.as_secs_f32()
> next_track_change
{
let ambience = self.ambience.read();
let track = ambience.tracks.iter().find(|track| track.tag == tag);
2022-03-20 07:07:23 +00:00
// set the channel's start point at this instant
2022-03-19 09:37:45 +00:00
audio.ambient_channels[index].set_began_playing(Instant::now());
if let Some(track) = track {
// set loop duration to the one specified in the ron
audio.ambient_channels[index].set_next_track_change(track.length);
// play the file of the current tag at the current multiplier
let current_multiplier =
audio.ambient_channels[index].get_multiplier();
audio.play_ambient(tag, &track.path, current_multiplier);
}
};
2022-03-19 09:37:45 +00:00
// remove channel if not playing
if audio.ambient_channels[index].get_multiplier() == 0.0 {
audio.ambient_channels[index].stop();
audio.ambient_channels.remove(index);
};
// move on to next tag
break;
} else {
// channel tag and current tag don't match, move on to next channel
continue;
}
2021-11-20 01:46:14 +00:00
}
2022-03-19 09:37:45 +00:00
} else {
// no need to run code at all, move on to the next tag
continue;
2021-11-20 01:46:14 +00:00
}
}
}
2022-03-19 09:37:45 +00:00
fn check_wind_necessity(&mut self, client: &Client, camera: &Camera) -> bool {
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);
return alt_multiplier * tree_multiplier > 0.0;
}
fn check_rain_necessity(&mut self, client: &Client, camera: &Camera) -> bool {
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);
client.weather_at_player().rain > 0.001 || camera_multiplier > 0.0
2022-03-20 07:07:23 +00:00
}
fn check_thunder_necessity(&mut self, client: &Client) -> bool {
2022-03-28 13:22:57 +00:00
client.weather_at_player().rain * 500.0 > 0.7
2022-03-20 07:07:23 +00:00
}
fn check_leaves_necessity(&mut self, client: &Client, camera: &Camera) -> bool {
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)
};
2022-03-23 06:50:38 +00:00
let tree_multiplier = 1.0
- (((1.0 - tree_density) + ((cam_pos.z - terrain_alt + 20.0).abs() / 150.0).powi(2))
.min(1.0));
2022-03-20 07:07:23 +00:00
return tree_multiplier > 0.1;
2022-03-19 09:37:45 +00:00
}
2021-11-20 01:46:14 +00:00
}
impl AmbientChannel {
pub fn maintain(&mut self, state: &State, client: &Client, camera: &Camera) -> f32 {
let tag = self.get_tag();
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let mut target_volume: f32 = match tag {
// Get target volume of wind
AmbientChannelTag::Wind => self.get_wind_volume(client, camera),
2022-03-20 07:07:23 +00:00
// Get target volume of rain
AmbientChannelTag::Rain => self.get_rain_volume(client, camera),
2022-03-20 07:07:23 +00:00
// Get target volume of thunder
AmbientChannelTag::Thunder => self.get_thunder_volume(client),
// Get target volume of leaves
AmbientChannelTag::Leaves => self.get_leaves_volume(client, camera),
};
target_volume = self.check_camera(state, client, cam_pos, target_volume);
return target_volume;
}
fn check_camera(
2021-11-20 01:46:14 +00:00
&mut self,
state: &State,
client: &Client,
cam_pos: Vec3<f32>,
initial_volume: f32,
) -> f32 {
let mut volume_multiplier = initial_volume;
let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
} else {
0.0
};
// Checks if the camera is underwater to stop 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;
}
// Is the camera roughly under the terrain?
if cam_pos.z < terrain_alt - 10.0 {
volume_multiplier = 0.0;
}
2021-11-20 01:46:14 +00:00
volume_multiplier.clamped(0.0, 1.0)
}
2021-11-20 01:46:14 +00:00
fn get_wind_volume(&mut self, client: &Client, camera: &Camera) -> f32 {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
2022-03-20 07:07:23 +00:00
// Float from around -30.0 to 30.0
2022-03-28 13:22:57 +00:00
let client_wind_speed_sq = client.weather_at_player().wind.magnitude_squared();
2020-11-07 01:55:59 +00:00
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);
2022-03-15 18:04:21 +00:00
// Lastly, we of course have to take into account actual wind speed from
// weathersim
2022-03-20 07:07:23 +00:00
let wind_speed_multiplier = (client_wind_speed_sq / 30.0_f32.powi(2)).min(1.0);
2022-03-15 18:04:21 +00:00
return alt_multiplier
* tree_multiplier
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0);
}
fn get_rain_volume(&mut self, client: &Client, camera: &Camera) -> f32 {
// multipler at end will have to change depending on how intense rain normally
// is
2022-03-23 06:50:38 +00:00
// TODO: make rain diminish with distance above terrain
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;
2022-03-23 06:50:38 +00:00
return rain_intensity.min(0.9);
2020-11-07 01:55:59 +00:00
}
2022-03-20 07:07:23 +00:00
fn get_thunder_volume(&mut self, client: &Client) -> f32 {
2022-03-28 13:22:57 +00:00
let thunder_intensity = client.weather_at_player().rain * 500.0;
2022-03-20 07:07:23 +00:00
if thunder_intensity < 0.7 {
0.0
} else {
thunder_intensity
}
}
fn get_leaves_volume(&mut self, 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 (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
} else {
(0.0, 0.0)
};
2022-03-23 06:50:38 +00:00
// 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));
2022-03-20 07:07:23 +00:00
if tree_multiplier > 0.1 {
2022-03-20 07:07:23 +00:00
tree_multiplier
} else {
0.0
}
}
2021-11-20 01:46:14 +00:00
}
2020-11-07 01:55:59 +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";
}