mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Underwater and cave reverb
This commit is contained in:
parent
695cc7f5cb
commit
f182002e38
@ -21,6 +21,7 @@ use crate::audio::{
|
|||||||
Listener,
|
Listener,
|
||||||
};
|
};
|
||||||
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
|
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
|
||||||
|
use std::time::Duration;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
@ -193,6 +194,27 @@ impl SfxChannel {
|
|||||||
self.sink.append(source);
|
self.sink.append(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn play_with_low_pass_filter<S>(&mut self, source: S)
|
||||||
|
where
|
||||||
|
S: Sized + Send + 'static,
|
||||||
|
S: Source<Item = f32>,
|
||||||
|
{
|
||||||
|
let source = source.low_pass(300);
|
||||||
|
self.sink.append(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_with_reverb<S: rodio::Source>(&mut self, source: S)
|
||||||
|
where
|
||||||
|
S: Sized + Send + 'static,
|
||||||
|
<S as Iterator>::Item: Sample,
|
||||||
|
<S as Iterator>::Item: Sync,
|
||||||
|
<S as Iterator>::Item: Send,
|
||||||
|
<S as std::iter::Iterator>::Item: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
let source = source.buffered().reverb(Duration::from_millis(200), 0.2);
|
||||||
|
self.sink.append(source);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
|
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
|
||||||
|
|
||||||
pub fn is_done(&self) -> bool { self.sink.empty() }
|
pub fn is_done(&self) -> bool { self.sink.empty() }
|
||||||
|
@ -192,6 +192,44 @@ impl AudioFrontend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Play (once) an sfx file by file path at the give position and volume
|
||||||
|
/// but with the sound passed through a low pass filter to simulate
|
||||||
|
/// underwater
|
||||||
|
pub fn play_underwater_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
||||||
|
if self.audio_stream.is_some() {
|
||||||
|
let sound = self
|
||||||
|
.sound_cache
|
||||||
|
.load_sound(sound)
|
||||||
|
.amplify(vol.unwrap_or(1.0));
|
||||||
|
|
||||||
|
let listener = self.listener.clone();
|
||||||
|
if let Some(channel) = self.get_sfx_channel() {
|
||||||
|
channel.set_pos(pos);
|
||||||
|
channel.update(&listener);
|
||||||
|
let sound = sound.convert_samples();
|
||||||
|
channel.play_with_low_pass_filter(sound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Play (once) an sfx file by file path at the give position and volume
|
||||||
|
/// but with reverb
|
||||||
|
pub fn play_reverb_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
||||||
|
if self.audio_stream.is_some() {
|
||||||
|
let sound = self
|
||||||
|
.sound_cache
|
||||||
|
.load_sound(sound)
|
||||||
|
.amplify(vol.unwrap_or(1.0));
|
||||||
|
|
||||||
|
let listener = self.listener.clone();
|
||||||
|
if let Some(channel) = self.get_sfx_channel() {
|
||||||
|
channel.set_pos(pos);
|
||||||
|
channel.update(&listener);
|
||||||
|
channel.play_with_reverb(sound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn play_wind(&mut self, sound: &str, volume_multiplier: f32) {
|
fn play_wind(&mut self, sound: &str, volume_multiplier: f32) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
if let Some(channel) = self.get_wind_channel(volume_multiplier) {
|
if let Some(channel) = self.get_wind_channel(volume_multiplier) {
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::EventMapper;
|
use super::EventMapper;
|
||||||
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::Pos, event::EventBus, spiral::Spiral2d, state::State, terrain::TerrainChunk,
|
comp::Pos, event::EventBus, spiral::Spiral2d, state::State, terrain::TerrainChunk,
|
||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
@ -43,6 +44,7 @@ impl EventMapper for BlockEventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
terrain: &Terrain<TerrainChunk>,
|
terrain: &Terrain<TerrainChunk>,
|
||||||
|
client: &Client,
|
||||||
) {
|
) {
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
|
|
||||||
@ -59,6 +61,12 @@ impl EventMapper for BlockEventMapper {
|
|||||||
(e.floor() as i32).div_euclid(sz as i32)
|
(e.floor() as i32).div_euclid(sz as i32)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// For determining if underground
|
||||||
|
let terrain_alt = match client.current_chunk() {
|
||||||
|
Some(chunk) => chunk.meta().alt(),
|
||||||
|
None => 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
struct BlockSounds<'a> {
|
struct BlockSounds<'a> {
|
||||||
// The function to select the blocks of interest that we should emit from
|
// The function to select the blocks of interest that we should emit from
|
||||||
blocks: fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>],
|
blocks: fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>],
|
||||||
@ -136,8 +144,13 @@ impl EventMapper for BlockEventMapper {
|
|||||||
|
|
||||||
// Iterate through each kind of block of interest
|
// Iterate through each kind of block of interest
|
||||||
for sounds in sounds.iter() {
|
for sounds in sounds.iter() {
|
||||||
|
// If the timing condition is false, continue
|
||||||
if !(sounds.cond)(state) {
|
if !(sounds.cond)(state) {
|
||||||
continue;
|
continue;
|
||||||
|
// If the player is underground, continue
|
||||||
|
} else if player_pos.0.z < (terrain_alt - 20.0) {
|
||||||
|
continue;
|
||||||
|
// Hack to reduce the number of birdcalls (too many leaf blocks)
|
||||||
} else if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
|
} else if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
|
||||||
&& thread_rng().gen_bool(0.995)
|
&& thread_rng().gen_bool(0.995)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::EventMapper;
|
use super::EventMapper;
|
||||||
|
|
||||||
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{object, Body, Pos},
|
comp::{object, Body, Pos},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
@ -43,6 +44,7 @@ impl EventMapper for CampfireEventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
_terrain: &Terrain<TerrainChunk>,
|
_terrain: &Terrain<TerrainChunk>,
|
||||||
|
_client: &Client,
|
||||||
) {
|
) {
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::EventMapper;
|
use super::EventMapper;
|
||||||
|
|
||||||
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{item::ItemKind, CharacterAbilityType, CharacterState, Loadout, Pos},
|
comp::{item::ItemKind, CharacterAbilityType, CharacterState, Loadout, Pos},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
@ -46,6 +47,7 @@ impl EventMapper for CombatEventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
_terrain: &Terrain<TerrainChunk>,
|
_terrain: &Terrain<TerrainChunk>,
|
||||||
|
_client: &Client,
|
||||||
) {
|
) {
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ mod combat;
|
|||||||
mod movement;
|
mod movement;
|
||||||
mod progression;
|
mod progression;
|
||||||
|
|
||||||
|
use client::Client;
|
||||||
use common::{state::State, terrain::TerrainChunk};
|
use common::{state::State, terrain::TerrainChunk};
|
||||||
|
|
||||||
use block::BlockEventMapper;
|
use block::BlockEventMapper;
|
||||||
@ -23,6 +24,7 @@ trait EventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
terrain: &Terrain<TerrainChunk>,
|
terrain: &Terrain<TerrainChunk>,
|
||||||
|
client: &Client,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,9 +52,10 @@ impl SfxEventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
terrain: &Terrain<TerrainChunk>,
|
terrain: &Terrain<TerrainChunk>,
|
||||||
|
client: &Client,
|
||||||
) {
|
) {
|
||||||
for mapper in &mut self.mappers {
|
for mapper in &mut self.mappers {
|
||||||
mapper.maintain(state, player_entity, camera, triggers, terrain);
|
mapper.maintain(state, player_entity, camera, triggers, terrain, client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
|
||||||
scene::{Camera, Terrain},
|
scene::{Camera, Terrain},
|
||||||
};
|
};
|
||||||
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
|
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
@ -48,6 +49,7 @@ impl EventMapper for MovementEventMapper {
|
|||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
_terrain: &Terrain<TerrainChunk>,
|
_terrain: &Terrain<TerrainChunk>,
|
||||||
|
_client: &Client,
|
||||||
) {
|
) {
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
scene::{Camera, Terrain},
|
scene::{Camera, Terrain},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use client::Client;
|
||||||
use common::{comp::Stats, event::EventBus, state::State, terrain::TerrainChunk};
|
use common::{comp::Stats, event::EventBus, state::State, terrain::TerrainChunk};
|
||||||
use specs::WorldExt;
|
use specs::WorldExt;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ impl EventMapper for ProgressionEventMapper {
|
|||||||
_camera: &Camera,
|
_camera: &Camera,
|
||||||
triggers: &SfxTriggers,
|
triggers: &SfxTriggers,
|
||||||
_terrain: &Terrain<TerrainChunk>,
|
_terrain: &Terrain<TerrainChunk>,
|
||||||
|
_client: &Client,
|
||||||
) {
|
) {
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ use crate::{
|
|||||||
scene::{Camera, Terrain},
|
scene::{Camera, Terrain},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
comp::{
|
comp::{
|
||||||
@ -96,7 +97,8 @@ use common::{
|
|||||||
event::EventBus,
|
event::EventBus,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
state::State,
|
state::State,
|
||||||
terrain::TerrainChunk,
|
terrain::{BlockKind, TerrainChunk},
|
||||||
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use event_mapper::SfxEventMapper;
|
use event_mapper::SfxEventMapper;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@ -243,6 +245,7 @@ impl SfxMgr {
|
|||||||
player_entity: specs::Entity,
|
player_entity: specs::Entity,
|
||||||
camera: &Camera,
|
camera: &Camera,
|
||||||
terrain: &Terrain<TerrainChunk>,
|
terrain: &Terrain<TerrainChunk>,
|
||||||
|
client: &Client,
|
||||||
) {
|
) {
|
||||||
if !audio.sfx_enabled() {
|
if !audio.sfx_enabled() {
|
||||||
return;
|
return;
|
||||||
@ -250,13 +253,30 @@ impl SfxMgr {
|
|||||||
|
|
||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||||
|
|
||||||
|
let cave = match client.current_chunk() {
|
||||||
|
Some(chunk) => chunk.meta().cave_alt() != 0.0,
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
let underwater = state
|
||||||
|
.terrain()
|
||||||
|
.get((camera.dependents().cam_pos + focus_off).map(|e| e.floor() as i32))
|
||||||
|
.map(|b| b.kind())
|
||||||
|
.unwrap_or(BlockKind::Air)
|
||||||
|
== BlockKind::Water;
|
||||||
let cam_pos = camera.dependents().cam_pos + focus_off;
|
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||||
|
|
||||||
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
||||||
|
|
||||||
// TODO: replace; deprecated in favor of outcomes
|
// TODO: replace; deprecated in favor of outcomes
|
||||||
self.event_mapper
|
self.event_mapper.maintain(
|
||||||
.maintain(state, player_entity, camera, &self.triggers, terrain);
|
state,
|
||||||
|
player_entity,
|
||||||
|
camera,
|
||||||
|
&self.triggers,
|
||||||
|
terrain,
|
||||||
|
client,
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: replace; deprecated in favor of outcomes
|
// TODO: replace; deprecated in favor of outcomes
|
||||||
let events = ecs.read_resource::<EventBus<SfxEventItem>>().recv_all();
|
let events = ecs.read_resource::<EventBus<SfxEventItem>>().recv_all();
|
||||||
@ -283,7 +303,14 @@ impl SfxMgr {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
audio.play_sfx(sfx_file, position, event.vol);
|
if underwater {
|
||||||
|
audio.play_underwater_sfx(sfx_file, position, event.vol);
|
||||||
|
} else if cave {
|
||||||
|
println!("Reverbbbbbbbbbb");
|
||||||
|
audio.play_reverb_sfx(sfx_file, position, event.vol);
|
||||||
|
} else {
|
||||||
|
audio.play_sfx(sfx_file, position, event.vol);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Missing sfx trigger config for sfx event. {:?}", event.sfx);
|
debug!("Missing sfx trigger config for sfx event. {:?}", event.sfx);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
//! Handles ambient wind sounds
|
//! Handles ambient wind sounds
|
||||||
use crate::audio::AudioFrontend;
|
use crate::{audio::AudioFrontend, scene::Camera};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{assets, state::State, terrain::BlockKind, vol::ReadVol};
|
||||||
assets,
|
|
||||||
state::State,
|
|
||||||
terrain::{BiomeKind, SitesKind},
|
|
||||||
};
|
|
||||||
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@ -65,18 +61,38 @@ impl WindMgr {
|
|||||||
|
|
||||||
/// Checks whether the previous track has completed. If so, sends a
|
/// Checks whether the previous track has completed. If so, sends a
|
||||||
/// request to play the next (random) track
|
/// request to play the next (random) track
|
||||||
pub fn maintain(&mut self, audio: &mut AudioFrontend, _state: &State, client: &Client) {
|
pub fn maintain(
|
||||||
|
&mut self,
|
||||||
|
audio: &mut AudioFrontend,
|
||||||
|
state: &State,
|
||||||
|
client: &Client,
|
||||||
|
camera: &Camera,
|
||||||
|
) {
|
||||||
if audio.sfx_enabled() && !self.soundtrack.tracks.is_empty() {
|
if audio.sfx_enabled() && !self.soundtrack.tracks.is_empty() {
|
||||||
let alt_multiplier = ((Self::get_current_alt(client) - 250.0) / 1500.0).min(0.0);
|
let alt_multiplier = ((Self::get_current_alt(client) - 250.0) / 1200.0).abs();
|
||||||
|
|
||||||
let tree_multiplier = 1.0 - Self::get_current_tree_density(client);
|
let tree_multiplier = 1.0 - Self::get_current_tree_density(client);
|
||||||
|
let mut volume_multiplier = alt_multiplier * tree_multiplier;
|
||||||
|
|
||||||
let volume_multiplier = alt_multiplier * tree_multiplier;
|
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||||
|
let cam_pos = camera.dependents().cam_pos + focus_off;
|
||||||
|
|
||||||
|
// Checks if the camera is underwater to stop wind sounds
|
||||||
|
if state
|
||||||
|
.terrain()
|
||||||
|
.get((cam_pos).map(|e| e.floor() as i32))
|
||||||
|
.map(|b| b.kind())
|
||||||
|
.unwrap_or(BlockKind::Air)
|
||||||
|
== BlockKind::Water
|
||||||
|
{
|
||||||
|
volume_multiplier = volume_multiplier * 0.1;
|
||||||
|
}
|
||||||
|
if cam_pos.z < Self::get_current_terrain_alt(client) {
|
||||||
|
volume_multiplier = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
audio.set_wind_volume(volume_multiplier);
|
audio.set_wind_volume(volume_multiplier);
|
||||||
|
|
||||||
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
|
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
|
||||||
println!("Got to wind maintain");
|
|
||||||
//let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
|
//let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
|
||||||
//let current_period_of_day = Self::get_current_day_period(game_time);
|
//let current_period_of_day = Self::get_current_day_period(game_time);
|
||||||
let track = &self.soundtrack.tracks[0];
|
let track = &self.soundtrack.tracks[0];
|
||||||
@ -84,14 +100,6 @@ impl WindMgr {
|
|||||||
self.began_playing = Instant::now();
|
self.began_playing = Instant::now();
|
||||||
self.next_track_change = track.length;
|
self.next_track_change = track.length;
|
||||||
|
|
||||||
//let alt_multiplier = (Self::get_current_alt(client)
|
|
||||||
// - Self::get_current_terrain_alt(client))
|
|
||||||
// / 1500.0;
|
|
||||||
|
|
||||||
//let tree_multiplier = 1.0 - Self::get_current_tree_density(client);
|
|
||||||
|
|
||||||
//let volume_multiplier = alt_multiplier * tree_multiplier;
|
|
||||||
|
|
||||||
audio.play_wind(&track.path, volume_multiplier);
|
audio.play_wind(&track.path, volume_multiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -997,9 +997,11 @@ impl Scene {
|
|||||||
scene_data.player_entity,
|
scene_data.player_entity,
|
||||||
&self.camera,
|
&self.camera,
|
||||||
&self.terrain,
|
&self.terrain,
|
||||||
|
client,
|
||||||
);
|
);
|
||||||
self.music_mgr.maintain(audio, scene_data.state, client);
|
self.music_mgr.maintain(audio, scene_data.state, client);
|
||||||
self.ambient_mgr.maintain(audio, scene_data.state, client);
|
self.ambient_mgr
|
||||||
|
.maintain(audio, scene_data.state, client, &self.camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the scene using the provided `Renderer`.
|
/// Render the scene using the provided `Renderer`.
|
||||||
|
Loading…
Reference in New Issue
Block a user