2020-11-14 00:27:09 +00:00
|
|
|
//! Handles ambient non-positional sounds
|
|
|
|
use crate::{
|
|
|
|
audio::{channel::AmbientChannelTag, AudioFrontend},
|
|
|
|
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;
|
|
|
|
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)]
|
2020-11-14 00:27:09 +00:00
|
|
|
struct AmbientCollection {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
pub struct AmbientWindMgr {
|
|
|
|
ambience: AssetHandle<AmbientCollection>,
|
2020-11-07 01:55:59 +00:00
|
|
|
began_playing: Instant,
|
|
|
|
next_track_change: f32,
|
2020-11-08 05:26:20 +00:00
|
|
|
volume: f32,
|
|
|
|
tree_multiplier: f32,
|
2020-11-07 01:55:59 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
pub struct AmbientRainMgr {
|
|
|
|
ambience: AssetHandle<AmbientCollection>,
|
|
|
|
began_playing: Instant,
|
|
|
|
next_track_change: f32,
|
|
|
|
volume: f32,
|
|
|
|
rain_intensity: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AmbientWindMgr {
|
2020-11-12 03:55:40 +00:00
|
|
|
fn default() -> Self {
|
2020-11-07 01:55:59 +00:00
|
|
|
Self {
|
2021-11-20 01:46:14 +00:00
|
|
|
ambience: load_ambience_items(),
|
2020-11-07 01:55:59 +00:00
|
|
|
began_playing: Instant::now(),
|
|
|
|
next_track_change: 0.0,
|
2020-11-08 05:26:20 +00:00
|
|
|
volume: 0.0,
|
|
|
|
tree_multiplier: 0.0,
|
2020-11-07 01:55:59 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-12 03:55:40 +00:00
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
impl Default for AmbientRainMgr {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
ambience: load_ambience_items(),
|
|
|
|
began_playing: Instant::now(),
|
|
|
|
next_track_change: 0.0,
|
|
|
|
volume: 0.0,
|
|
|
|
rain_intensity: 0.0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AmbientWindMgr {
|
2020-11-07 01:55:59 +00:00
|
|
|
/// Checks whether the previous track has completed. If so, sends a
|
|
|
|
/// request to play the next (random) track
|
2020-11-07 10:02:29 +00:00
|
|
|
pub fn maintain(
|
|
|
|
&mut self,
|
|
|
|
audio: &mut AudioFrontend,
|
|
|
|
state: &State,
|
|
|
|
client: &Client,
|
|
|
|
camera: &Camera,
|
|
|
|
) {
|
2021-11-20 01:46:14 +00:00
|
|
|
if audio.sfx_enabled() && !self.ambience.read().tracks.is_empty() {
|
2020-11-12 03:55:40 +00:00
|
|
|
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
|
|
|
let cam_pos = camera.dependents().cam_pos + focus_off;
|
|
|
|
|
2020-11-17 00:38:48 +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)
|
|
|
|
};
|
2020-11-08 05:26:20 +00:00
|
|
|
|
2020-11-14 00:27:09 +00:00
|
|
|
// The following code is specifically for wind, as it is the only
|
|
|
|
// non-positional ambient sound in the game. Others can be added
|
|
|
|
// as seen fit.
|
|
|
|
|
2020-11-27 17:43:47 +00:00
|
|
|
let target_volume = {
|
|
|
|
// 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.
|
|
|
|
self.tree_multiplier = ((1.0 - tree_density)
|
|
|
|
+ ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))
|
|
|
|
.min(1.0);
|
|
|
|
|
|
|
|
let mut volume_multiplier = alt_multiplier * self.tree_multiplier;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2021-11-20 01:46:14 +00:00
|
|
|
// Is the camera roughly under the terrain?
|
2020-11-27 17:43:47 +00:00
|
|
|
if cam_pos.z < terrain_alt - 10.0 {
|
|
|
|
volume_multiplier = 0.0;
|
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
|
2020-11-27 17:43:47 +00:00
|
|
|
volume_multiplier.clamped(0.0, 1.0)
|
|
|
|
};
|
2020-11-08 05:26:20 +00:00
|
|
|
|
2020-11-14 00:27:09 +00:00
|
|
|
// Transitions the ambient sounds (more) smoothly
|
2021-11-20 01:46:14 +00:00
|
|
|
if audio.get_ambient_channel(AmbientChannelTag::Wind).is_none() {
|
|
|
|
audio.new_ambient_channel(AmbientChannelTag::Wind);
|
|
|
|
} else {
|
|
|
|
self.volume = audio.get_ambient_volume(AmbientChannelTag::Wind);
|
|
|
|
audio.set_ambient_volume(
|
|
|
|
AmbientChannelTag::Wind,
|
|
|
|
Lerp::lerp(self.volume, target_volume, 0.01),
|
|
|
|
);
|
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
|
|
|
|
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
|
2021-11-20 01:46:14 +00:00
|
|
|
let ambience = self.ambience.read();
|
|
|
|
let wind_track = &ambience
|
2020-11-14 00:27:09 +00:00
|
|
|
.tracks
|
|
|
|
.iter()
|
2020-11-17 00:38:48 +00:00
|
|
|
.find(|track| track.tag == AmbientChannelTag::Wind);
|
2021-11-20 01:46:14 +00:00
|
|
|
self.began_playing = Instant::now();
|
|
|
|
if let Some(wind_track) = wind_track {
|
|
|
|
self.next_track_change = wind_track.length;
|
|
|
|
audio.play_ambient(AmbientChannelTag::Wind, &wind_track.path, target_volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AmbientRainMgr {
|
|
|
|
/// Checks whether the previous track has completed. If so, sends a
|
|
|
|
/// request to play the next (random) track
|
|
|
|
pub fn maintain(
|
|
|
|
&mut self,
|
|
|
|
audio: &mut AudioFrontend,
|
|
|
|
state: &State,
|
|
|
|
client: &Client,
|
|
|
|
camera: &Camera,
|
|
|
|
) {
|
|
|
|
if audio.sfx_enabled() && !self.ambience.read().tracks.is_empty() {
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
// multipler at end will have to change depending on how intense rain normally
|
|
|
|
// is
|
|
|
|
self.rain_intensity = client.current_weather().rain * 5.0;
|
|
|
|
|
|
|
|
let mut volume_multiplier = self.rain_intensity;
|
|
|
|
|
|
|
|
// TODO: make rain diminish with distance above terrain
|
|
|
|
let target_volume = {
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume_multiplier = volume_multiplier.clamped(0.0, 1.0);
|
|
|
|
|
|
|
|
// possibly remove noise
|
|
|
|
if volume_multiplier < 0.05 {
|
|
|
|
0.0
|
|
|
|
} else {
|
|
|
|
volume_multiplier
|
|
|
|
}
|
|
|
|
};
|
2020-11-14 00:27:09 +00:00
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
// Transitions the ambient sounds (more) smoothly
|
|
|
|
if audio.get_ambient_channel(AmbientChannelTag::Rain).is_none() {
|
|
|
|
audio.new_ambient_channel(AmbientChannelTag::Rain);
|
|
|
|
} else {
|
|
|
|
self.volume = audio.get_ambient_volume(AmbientChannelTag::Rain);
|
|
|
|
audio.set_ambient_volume(
|
|
|
|
AmbientChannelTag::Rain,
|
|
|
|
Lerp::lerp(self.volume, target_volume, 0.01),
|
|
|
|
);
|
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
|
|
|
|
let ambience = self.ambience.read();
|
|
|
|
let rain_track = &ambience
|
|
|
|
.tracks
|
|
|
|
.iter()
|
|
|
|
.find(|track| track.tag == AmbientChannelTag::Rain);
|
|
|
|
self.began_playing = Instant::now();
|
|
|
|
if let Some(rain_track) = rain_track {
|
|
|
|
self.next_track_change = rain_track.length;
|
|
|
|
audio.play_ambient(AmbientChannelTag::Rain, &rain_track.path, target_volume);
|
2020-11-14 00:27:09 +00:00
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-20 01:46:14 +00:00
|
|
|
}
|
2020-11-07 01:55:59 +00:00
|
|
|
|
2021-11-20 01:46:14 +00:00
|
|
|
fn load_ambience_items() -> AssetHandle<AmbientCollection> {
|
|
|
|
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";
|
|
|
|
}
|