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,
|
||||
};
|
||||
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
@ -193,6 +194,27 @@ impl SfxChannel {
|
||||
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 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) {
|
||||
if self.audio_stream.is_some() {
|
||||
if let Some(channel) = self.get_wind_channel(volume_multiplier) {
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::EventMapper;
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::Pos, event::EventBus, spiral::Spiral2d, state::State, terrain::TerrainChunk,
|
||||
vol::RectRasterableVol,
|
||||
@ -43,6 +44,7 @@ impl EventMapper for BlockEventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
@ -59,6 +61,12 @@ impl EventMapper for BlockEventMapper {
|
||||
(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> {
|
||||
// The function to select the blocks of interest that we should emit from
|
||||
blocks: fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>],
|
||||
@ -136,8 +144,13 @@ impl EventMapper for BlockEventMapper {
|
||||
|
||||
// Iterate through each kind of block of interest
|
||||
for sounds in sounds.iter() {
|
||||
// If the timing condition is false, continue
|
||||
if !(sounds.cond)(state) {
|
||||
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)
|
||||
&& thread_rng().gen_bool(0.995)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
|
||||
use super::EventMapper;
|
||||
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{object, Body, Pos},
|
||||
event::EventBus,
|
||||
@ -43,6 +44,7 @@ impl EventMapper for CampfireEventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
_terrain: &Terrain<TerrainChunk>,
|
||||
_client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
|
||||
use super::EventMapper;
|
||||
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{item::ItemKind, CharacterAbilityType, CharacterState, Loadout, Pos},
|
||||
event::EventBus,
|
||||
@ -46,6 +47,7 @@ impl EventMapper for CombatEventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
_terrain: &Terrain<TerrainChunk>,
|
||||
_client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
|
@ -4,6 +4,7 @@ mod combat;
|
||||
mod movement;
|
||||
mod progression;
|
||||
|
||||
use client::Client;
|
||||
use common::{state::State, terrain::TerrainChunk};
|
||||
|
||||
use block::BlockEventMapper;
|
||||
@ -23,6 +24,7 @@ trait EventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
);
|
||||
}
|
||||
|
||||
@ -50,9 +52,10 @@ impl SfxEventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
) {
|
||||
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},
|
||||
scene::{Camera, Terrain},
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{Body, CharacterState, PhysicsState, Pos, Vel},
|
||||
event::EventBus,
|
||||
@ -48,6 +49,7 @@ impl EventMapper for MovementEventMapper {
|
||||
camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
_terrain: &Terrain<TerrainChunk>,
|
||||
_client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
scene::{Camera, Terrain},
|
||||
};
|
||||
|
||||
use client::Client;
|
||||
use common::{comp::Stats, event::EventBus, state::State, terrain::TerrainChunk};
|
||||
use specs::WorldExt;
|
||||
|
||||
@ -33,6 +34,7 @@ impl EventMapper for ProgressionEventMapper {
|
||||
_camera: &Camera,
|
||||
triggers: &SfxTriggers,
|
||||
_terrain: &Terrain<TerrainChunk>,
|
||||
_client: &Client,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
|
@ -87,6 +87,7 @@ use crate::{
|
||||
scene::{Camera, Terrain},
|
||||
};
|
||||
|
||||
use client::Client;
|
||||
use common::{
|
||||
assets,
|
||||
comp::{
|
||||
@ -96,7 +97,8 @@ use common::{
|
||||
event::EventBus,
|
||||
outcome::Outcome,
|
||||
state::State,
|
||||
terrain::TerrainChunk,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use event_mapper::SfxEventMapper;
|
||||
use hashbrown::HashMap;
|
||||
@ -243,6 +245,7 @@ impl SfxMgr {
|
||||
player_entity: specs::Entity,
|
||||
camera: &Camera,
|
||||
terrain: &Terrain<TerrainChunk>,
|
||||
client: &Client,
|
||||
) {
|
||||
if !audio.sfx_enabled() {
|
||||
return;
|
||||
@ -250,13 +253,30 @@ impl SfxMgr {
|
||||
|
||||
let ecs = state.ecs();
|
||||
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;
|
||||
|
||||
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
|
||||
|
||||
// TODO: replace; deprecated in favor of outcomes
|
||||
self.event_mapper
|
||||
.maintain(state, player_entity, camera, &self.triggers, terrain);
|
||||
self.event_mapper.maintain(
|
||||
state,
|
||||
player_entity,
|
||||
camera,
|
||||
&self.triggers,
|
||||
terrain,
|
||||
client,
|
||||
);
|
||||
|
||||
// TODO: replace; deprecated in favor of outcomes
|
||||
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 {
|
||||
debug!("Missing sfx trigger config for sfx event. {:?}", event.sfx);
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
//! Handles ambient wind sounds
|
||||
use crate::audio::AudioFrontend;
|
||||
use crate::{audio::AudioFrontend, scene::Camera};
|
||||
use client::Client;
|
||||
use common::{
|
||||
assets,
|
||||
state::State,
|
||||
terrain::{BiomeKind, SitesKind},
|
||||
};
|
||||
use common::{assets, state::State, terrain::BlockKind, vol::ReadVol};
|
||||
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
||||
use serde::Deserialize;
|
||||
use std::time::Instant;
|
||||
@ -65,18 +61,38 @@ impl WindMgr {
|
||||
|
||||
/// 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) {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
client: &Client,
|
||||
camera: &Camera,
|
||||
) {
|
||||
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 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);
|
||||
|
||||
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 current_period_of_day = Self::get_current_day_period(game_time);
|
||||
let track = &self.soundtrack.tracks[0];
|
||||
@ -84,14 +100,6 @@ impl WindMgr {
|
||||
self.began_playing = Instant::now();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -997,9 +997,11 @@ impl Scene {
|
||||
scene_data.player_entity,
|
||||
&self.camera,
|
||||
&self.terrain,
|
||||
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`.
|
||||
|
Loading…
Reference in New Issue
Block a user