mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'DaforLynx/thunder-fix' into 'master'
Lightning strikes are less audible; cave music restored; fixed audio permanently stopping on logout See merge request veloren/veloren!3487
This commit is contained in:
commit
7f94580b20
@ -2,22 +2,22 @@
|
||||
tracks: [
|
||||
(
|
||||
path: "voxygen.audio.ambience.wind",
|
||||
length: 14.17,
|
||||
length: 14.2,
|
||||
tag: Wind,
|
||||
),
|
||||
(
|
||||
path: "voxygen.audio.ambience.rain",
|
||||
length: 16.97,
|
||||
length: 17.0,
|
||||
tag: Rain,
|
||||
),
|
||||
(
|
||||
path:"voxygen.audio.ambience.thunder",
|
||||
length: 31.97,
|
||||
length: 32.0,
|
||||
tag: Thunder,
|
||||
),
|
||||
(
|
||||
path:"voxygen.audio.ambience.leaves",
|
||||
length: 25.97,
|
||||
length: 26.0,
|
||||
tag: Leaves,
|
||||
),
|
||||
]
|
||||
|
BIN
assets/voxygen/audio/sfx/ambient/lightning_1.ogg
(Stored with Git LFS)
BIN
assets/voxygen/audio/sfx/ambient/lightning_1.ogg
(Stored with Git LFS)
Binary file not shown.
@ -1496,20 +1496,22 @@ impl Client {
|
||||
if let Some(position) = self.current::<comp::Pos>() {
|
||||
player_alt = position.0.z;
|
||||
}
|
||||
let mut contains_cave = false;
|
||||
//let mut contains_cave = false;
|
||||
let mut terrain_alt = 0.0;
|
||||
let mut contains_dungeon = false;
|
||||
let mut contains_settlement = false;
|
||||
if let Some(chunk) = self.current_chunk() {
|
||||
terrain_alt = chunk.meta().alt();
|
||||
contains_cave = chunk.meta().contains_cave();
|
||||
//contains_cave = chunk.meta().contains_cave();
|
||||
contains_dungeon = chunk.meta().contains_dungeon();
|
||||
contains_settlement = chunk.meta().contains_settlement();
|
||||
}
|
||||
if player_alt < (terrain_alt - 25.0) && contains_cave {
|
||||
SitesKind::Cave
|
||||
} else if player_alt < (terrain_alt - 25.0) && contains_dungeon {
|
||||
SitesKind::Dungeon
|
||||
if player_alt < terrain_alt - 40.0 {
|
||||
if contains_dungeon {
|
||||
SitesKind::Dungeon
|
||||
} else {
|
||||
SitesKind::Cave
|
||||
}
|
||||
} else if contains_settlement {
|
||||
SitesKind::Settlement
|
||||
} else {
|
||||
|
@ -89,7 +89,11 @@ impl AmbientMgr {
|
||||
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 * ambience_volume);
|
||||
audio.play_ambient(
|
||||
tag,
|
||||
&track.path,
|
||||
Some(current_multiplier * ambience_volume),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -103,6 +107,15 @@ impl AmbientMgr {
|
||||
}
|
||||
|
||||
impl AmbientChannelTag {
|
||||
pub fn tag_max_volume(tag: AmbientChannelTag) -> f32 {
|
||||
match tag {
|
||||
AmbientChannelTag::Wind => 1.15,
|
||||
AmbientChannelTag::Rain => 0.95,
|
||||
AmbientChannelTag::Thunder => 1.33,
|
||||
AmbientChannelTag::Leaves => 1.33,
|
||||
}
|
||||
}
|
||||
|
||||
// Gets appropriate volume for each tag
|
||||
pub fn get_tag_volume(tag: AmbientChannelTag, client: &Client, camera: &Camera) -> f32 {
|
||||
match tag {
|
||||
@ -134,11 +147,10 @@ impl AmbientChannelTag {
|
||||
/ 15.0_f32.powi(2))
|
||||
.min(1.33);
|
||||
|
||||
((alt_multiplier
|
||||
(alt_multiplier
|
||||
* tree_multiplier
|
||||
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)))
|
||||
+ ((alt_multiplier * 0.15) * tree_multiplier))
|
||||
.min(1.15)
|
||||
+ (alt_multiplier * 0.15) * tree_multiplier
|
||||
},
|
||||
AmbientChannelTag::Rain => {
|
||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||
@ -153,10 +165,10 @@ impl AmbientChannelTag {
|
||||
let camera_multiplier =
|
||||
1.0 - ((cam_pos.z - terrain_alt).abs() / 75.0).powi(2).min(1.0);
|
||||
|
||||
((client.weather_at_player().rain * 2.5) * camera_multiplier).min(0.95)
|
||||
(client.weather_at_player().rain * 3.0) * camera_multiplier
|
||||
},
|
||||
AmbientChannelTag::Thunder => {
|
||||
let rain_intensity = (client.weather_at_player().rain * 2.5).min(1.33);
|
||||
let rain_intensity = client.weather_at_player().rain * 3.0;
|
||||
|
||||
if rain_intensity < 0.7 {
|
||||
0.0
|
||||
@ -179,8 +191,8 @@ impl AmbientChannelTag {
|
||||
// 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));
|
||||
+ ((cam_pos.z - terrain_alt - 20.0).abs() / 150.0).powi(2))
|
||||
.min(1.1));
|
||||
|
||||
// Take into account wind speed too, which amplifies tree noise
|
||||
let wind_speed_multiplier = (client.weather_at_player().wind.magnitude_squared()
|
||||
@ -188,7 +200,7 @@ impl AmbientChannelTag {
|
||||
.min(1.0);
|
||||
|
||||
if tree_multiplier > 0.1 {
|
||||
(tree_multiplier * (1.0 + wind_speed_multiplier)).min(1.33)
|
||||
tree_multiplier * (1.0 + wind_speed_multiplier)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
@ -225,7 +237,8 @@ fn get_target_volume(
|
||||
}
|
||||
|
||||
// Is the camera underneath the terrain? Fade out the lower it goes beneath.
|
||||
volume_multiplier * ((cam_pos.z - terrain_alt) / 30.0 + 1.0).clamped(0.0, 1.0)
|
||||
(volume_multiplier * ((cam_pos.z - terrain_alt) / 40.0 + 1.0).clamped(0.0, 1.0))
|
||||
.min(AmbientChannelTag::tag_max_volume(tag))
|
||||
}
|
||||
|
||||
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {
|
||||
|
@ -282,6 +282,11 @@ impl SfxChannel {
|
||||
}
|
||||
}
|
||||
|
||||
/// An UiChannel uses a non-spatial audio sink, and is designed for short-lived
|
||||
/// audio which is not spatially controlled, but does not need control over
|
||||
/// playback or fading/transitions
|
||||
///
|
||||
/// See also: [`Rodio::Sink`](https://docs.rs/rodio/0.11.0/rodio/struct.Sink.html)
|
||||
pub struct UiChannel {
|
||||
sink: Sink,
|
||||
}
|
||||
|
@ -21,6 +21,11 @@ use common::assets::{AssetExt, AssetHandle};
|
||||
use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError};
|
||||
use vek::*;
|
||||
|
||||
/// Prevents sounds that are too low in volume from playing. This may marginally
|
||||
/// improve performance in certain situations since less audio channels will be
|
||||
/// used on average.
|
||||
const MIN_HEARABLE_VOLUME: f32 = 0.003;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Listener {
|
||||
pos: Vec3<f32>,
|
||||
@ -216,20 +221,14 @@ impl AudioFrontend {
|
||||
self.music_channels.last_mut()
|
||||
}
|
||||
|
||||
/// Play an sfx file given the position, SfxEvent, and whether it is
|
||||
/// underwater or not
|
||||
pub fn emit_sfx(
|
||||
&mut self,
|
||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
||||
position: Vec3<f32>,
|
||||
volume: Option<f32>,
|
||||
underwater: bool,
|
||||
) {
|
||||
if let Some((event, item)) = trigger_item {
|
||||
// Find sound based on given trigger_item
|
||||
// Randomizes if multiple sounds are found
|
||||
// Errors if no sounds are found
|
||||
let sfx_file = match item.files.len() {
|
||||
/// Find sound based on given trigger_item
|
||||
/// Randomizes if multiple sounds are found
|
||||
/// Errors if no sounds are found
|
||||
fn get_sfx_file<'a>(
|
||||
trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
|
||||
) -> Option<&'a str> {
|
||||
trigger_item.map(|(event, item)| {
|
||||
match item.files.len() {
|
||||
0 => {
|
||||
debug!("Sfx event {:?} is missing audio file.", event);
|
||||
"voxygen.audio.sfx.placeholder"
|
||||
@ -243,9 +242,22 @@ impl AudioFrontend {
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Play an sfx file given the position, SfxEvent, and whether it is
|
||||
/// underwater or not
|
||||
pub fn emit_sfx(
|
||||
&mut self,
|
||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
||||
position: Vec3<f32>,
|
||||
volume: Option<f32>,
|
||||
underwater: bool,
|
||||
) {
|
||||
if let Some(sfx_file) = Self::get_sfx_file(trigger_item) {
|
||||
// Play sound in empty channel at given position
|
||||
if self.audio_stream.is_some() {
|
||||
if self.audio_stream.is_some() && volume.map_or(true, |v| v > MIN_HEARABLE_VOLUME) {
|
||||
let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0));
|
||||
|
||||
let listener = self.listener.clone();
|
||||
@ -275,27 +287,9 @@ impl AudioFrontend {
|
||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
||||
volume: Option<f32>,
|
||||
) {
|
||||
// Find sound based on given trigger_item
|
||||
// Randomizes if multiple sounds are found
|
||||
// Errors if no sounds are found
|
||||
if let Some((event, item)) = trigger_item {
|
||||
let sfx_file = match item.files.len() {
|
||||
0 => {
|
||||
debug!("Sfx event {:?} is missing audio file.", event);
|
||||
"voxygen.audio.sfx.placeholder"
|
||||
},
|
||||
1 => item
|
||||
.files
|
||||
.last()
|
||||
.expect("Failed to determine sound file for this trigger item."),
|
||||
_ => {
|
||||
// If more than one file is listed, choose one at random
|
||||
let rand_step = rand::random::<usize>() % item.files.len();
|
||||
&item.files[rand_step]
|
||||
},
|
||||
};
|
||||
if let Some(sfx_file) = Self::get_sfx_file(trigger_item) {
|
||||
// Play sound in empty channel
|
||||
if self.audio_stream.is_some() {
|
||||
if self.audio_stream.is_some() && volume.map_or(true, |v| v > MIN_HEARABLE_VOLUME) {
|
||||
let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0));
|
||||
|
||||
if let Some(channel) = self.get_ui_channel() {
|
||||
@ -308,10 +302,10 @@ impl AudioFrontend {
|
||||
}
|
||||
|
||||
/// Plays a file at a given volume in the channel with a given tag
|
||||
fn play_ambient(&mut self, channel_tag: AmbientChannelTag, sound: &str, volume: f32) {
|
||||
fn play_ambient(&mut self, channel_tag: AmbientChannelTag, sound: &str, volume: Option<f32>) {
|
||||
if self.audio_stream.is_some() {
|
||||
if let Some(channel) = self.get_ambient_channel(channel_tag) {
|
||||
channel.set_volume(volume);
|
||||
channel.set_volume(volume.unwrap_or(1.0));
|
||||
channel.play(load_ogg(sound));
|
||||
}
|
||||
}
|
||||
@ -503,19 +497,18 @@ impl AudioFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_ambient_sounds(&mut self) {
|
||||
for channel in self.ambient_channels.iter_mut() {
|
||||
channel.stop()
|
||||
}
|
||||
}
|
||||
pub fn stop_all_ambience(&mut self) { self.ambient_channels.retain(|x| Some(x).is_none()) }
|
||||
|
||||
// Sfx channels do not repopulate themselves yet
|
||||
pub fn stop_all_sfx(&mut self) {
|
||||
for channel in self.sfx_channels.iter_mut() {
|
||||
channel.stop()
|
||||
}
|
||||
for channel in self.ui_channels.iter_mut() {
|
||||
channel.stop()
|
||||
}
|
||||
if let Some(audio_stream) = &self.audio_stream {
|
||||
for channel in &mut self.sfx_channels {
|
||||
*channel = SfxChannel::new(audio_stream);
|
||||
}
|
||||
for channel in &mut self.ui_channels {
|
||||
*channel = UiChannel::new(audio_stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The following is for the disabled device switcher
|
||||
|
@ -435,13 +435,13 @@ impl SfxMgr {
|
||||
);
|
||||
},
|
||||
Outcome::Lightning { pos } => {
|
||||
let power = (1.0 - pos.distance(audio.listener.pos) / 6_000.0)
|
||||
.clamped(0.0, 1.0)
|
||||
.powf(0.75);
|
||||
let power = (1.0 - pos.distance(audio.listener.pos) / 5_000.0)
|
||||
.max(0.0)
|
||||
.powi(7);
|
||||
if power > 0.0 {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Lightning);
|
||||
// TODO: Don't use UI sfx, add a way to control position falloff
|
||||
audio.emit_ui_sfx(sfx_trigger_item, Some(power * 3.0));
|
||||
audio.emit_ui_sfx(sfx_trigger_item, Some((power * 3.0).min(2.9)));
|
||||
}
|
||||
},
|
||||
Outcome::GroundSlam { pos, .. } => {
|
||||
|
@ -1206,7 +1206,7 @@ impl PlayState for SessionState {
|
||||
// Stop all sounds
|
||||
// TODO: Abstract this behavior to all instances of PlayStateResult::Pop
|
||||
// somehow
|
||||
global_state.audio.stop_ambient_sounds();
|
||||
global_state.audio.stop_all_ambience();
|
||||
global_state.audio.stop_all_sfx();
|
||||
return PlayStateResult::Pop;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user