From ac82689f83ceeabc12e29e366beba6259d42f16c Mon Sep 17 00:00:00 2001 From: DaforLynx Date: Fri, 3 Jun 2022 18:25:17 -0700 Subject: [PATCH] Clean up audio code, fix egui bug --- assets/voxygen/audio/ambience/leaves.ogg | 4 +- assets/voxygen/audio/ambient.ron | 2 +- assets/voxygen/shaders/clouds-frag.glsl | 69 +++--- assets/voxygen/shaders/include/lod.glsl | 2 +- .../shaders/include/rain_occlusion.glsl | 6 +- assets/voxygen/shaders/include/sky.glsl | 6 +- .../shaders/rain-occlusion-directed-vert.glsl | 6 +- voxygen/Cargo.toml | 4 +- voxygen/src/audio/ambient.rs | 216 +++++++----------- voxygen/src/audio/channel.rs | 28 +-- voxygen/src/audio/mod.rs | 166 +++++++------- voxygen/src/audio/music.rs | 5 + voxygen/src/audio/sfx/mod.rs | 5 +- voxygen/src/hud/mod.rs | 8 +- voxygen/src/render/pipelines/lod_terrain.rs | 8 +- voxygen/src/render/pipelines/mod.rs | 3 +- .../src/render/pipelines/rain_occlusion.rs | 22 +- voxygen/src/render/renderer.rs | 16 +- voxygen/src/render/renderer/drawer.rs | 12 +- .../src/render/renderer/rain_occlusion_map.rs | 6 +- voxygen/src/scene/mod.rs | 34 ++- voxygen/src/scene/terrain.rs | 12 +- voxygen/src/session/mod.rs | 7 +- 23 files changed, 295 insertions(+), 352 deletions(-) diff --git a/assets/voxygen/audio/ambience/leaves.ogg b/assets/voxygen/audio/ambience/leaves.ogg index a5ac1765f6..22276e12f7 100644 --- a/assets/voxygen/audio/ambience/leaves.ogg +++ b/assets/voxygen/audio/ambience/leaves.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63a218856961a57daf7b81ee25a6440cce9d76121dc6c48861e96211741c7d62 -size 454547 +oid sha256:30a6d9bc0e63f7216eaaddef5d9ada465f241b2f1206b522dd388a552dba5708 +size 461250 diff --git a/assets/voxygen/audio/ambient.ron b/assets/voxygen/audio/ambient.ron index 4d02185ce8..a1fb6a9d48 100644 --- a/assets/voxygen/audio/ambient.ron +++ b/assets/voxygen/audio/ambient.ron @@ -17,7 +17,7 @@ ), ( path:"voxygen.audio.ambience.leaves", - length: 27.0, + length: 26.0, tag: Leaves, ), ] diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl index 5c5e842537..75998d2cd4 100644 --- a/assets/voxygen/shaders/clouds-frag.glsl +++ b/assets/voxygen/shaders/clouds-frag.glsl @@ -108,44 +108,47 @@ void main() { vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z); vec3 cam_wpos = cam_pos.xyz + focus_off.xyz; - float rain_dist = 50.0; - for (int i = 0; i < 4; i ++) { - float old_rain_dist = rain_dist; - rain_dist *= 0.3; + float rain = rain_density_at(cam_wpos.xy); + if (rain > 0.0) { + float rain_dist = 50.0; + for (int i = 0; i < 4; i ++) { + float old_rain_dist = rain_dist; + rain_dist *= 0.3; - vec2 drop_density = vec2(30, 1); + vec2 drop_density = vec2(30, 1); - vec2 rain_pos = (view_pos * rain_dist); - rain_pos.y += integrated_rain_vel; + vec2 rain_pos = (view_pos * rain_dist); + rain_pos.y += integrated_rain_vel; - vec2 cell = floor(rain_pos * drop_density) / drop_density; + vec2 cell = floor(rain_pos * drop_density) / drop_density; - float drop_depth = mix( - old_rain_dist, - rain_dist, - fract(hash(fract(vec4(cell, rain_dist, 0) * 0.1))) - ); - vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * drop_depth; - float dist_to_rain = length(rpos); - if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) { - continue; + float drop_depth = mix( + old_rain_dist, + rain_dist, + fract(hash(fract(vec4(cell, rain_dist, 0) * 0.1))) + ); + vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * drop_depth; + float dist_to_rain = length(rpos); + if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) { + continue; + } + + if (dot(rpos * vec3(1, 1, 0.5), rpos) < 1.0) { + break; + } + float rain_density = rain * rain_occlusion_at(cam_pos.xyz + rpos.xyz) * 10.0; + + if (rain_density < 0.001 || fract(hash(fract(vec4(cell, rain_dist, 0) * 0.01))) > rain_density) { + continue; + } + vec2 near_drop = cell + (vec2(0.5) + (vec2(hash(vec4(cell, 0, 0)), 0.5) - 0.5) * vec2(2, 0)) / drop_density; + + vec2 drop_size = vec2(0.0008, 0.03); + float avg_alpha = (drop_size.x * drop_size.y) * 10 / 1; + float alpha = sign(max(1 - length((rain_pos - near_drop) / drop_size * 0.1), 0)); + float light = sqrt(dot(old_color, vec3(1))) + (get_sun_brightness() + get_moon_brightness()) * 0.01; + color.rgb = mix(color.rgb, vec3(0.3, 0.4, 0.5) * light, mix(avg_alpha, alpha, min(1000 / dist_to_rain, 1)) * 0.25); } - - if (dot(rpos * vec3(1, 1, 0.5), rpos) < 1.0) { - break; - } - float rain_density = rain_density_at(cam_wpos.xy + rpos.xy) * rain_occlusion_at(cam_pos.xyz + rpos.xyz) * 10.0; - - if (rain_density < 0.001 || fract(hash(fract(vec4(cell, rain_dist, 0) * 0.01))) > rain_density) { - continue; - } - vec2 near_drop = cell + (vec2(0.5) + (vec2(hash(vec4(cell, 0, 0)), 0.5) - 0.5) * vec2(2, 0)) / drop_density; - - vec2 drop_size = vec2(0.0008, 0.03); - float avg_alpha = (drop_size.x * drop_size.y) * 10 / 1; - float alpha = sign(max(1 - length((rain_pos - near_drop) / drop_size * 0.1), 0)); - float light = sqrt(dot(old_color, vec3(1))) + (get_sun_brightness() + get_moon_brightness()) * 0.01; - color.rgb = mix(color.rgb, vec3(0.3, 0.4, 0.5) * light, mix(avg_alpha, alpha, min(1000 / dist_to_rain, 1)) * 0.25); } tgt_color = vec4(color.rgb, 1); diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl index f5c408b0ef..e56e2f5010 100644 --- a/assets/voxygen/shaders/include/lod.glsl +++ b/assets/voxygen/shaders/include/lod.glsl @@ -125,7 +125,7 @@ vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) { // Gets the altitude at a position relative to focus_off. float alt_at(vec2 pos) { - vec4 alt_sample = textureLod/*textureBicubic16*/(sampler2D(t_alt, s_alt), wpos_to_uv(t_alt, s_alt, focus_off.xy + pos), 0); + vec4 alt_sample = textureLod/*textureBicubic16*/(sampler2D(t_alt, s_alt), wpos_to_uv(focus_off.xy + pos), 0); return (/*round*/((alt_sample.r / 256.0 + alt_sample.g) * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; diff --git a/assets/voxygen/shaders/include/rain_occlusion.glsl b/assets/voxygen/shaders/include/rain_occlusion.glsl index 8768489481..78d5c7d8b7 100644 --- a/assets/voxygen/shaders/include/rain_occlusion.glsl +++ b/assets/voxygen/shaders/include/rain_occlusion.glsl @@ -10,8 +10,8 @@ uniform samplerShadow s_directed_occlusion_maps; layout (std140, set = 0, binding = 14) uniform u_rain_occlusion { - mat4 occlusionMatrices; - mat4 occlusion_texture_mat; + mat4 rain_occlusion_matrices; + mat4 rain_occlusion_texture_mat; mat4 rel_rain_dir_mat; float integrated_rain_vel; vec3 occlusion_dummy; // Fix alignment. @@ -21,7 +21,7 @@ float rain_occlusion_at(in vec3 fragPos) { float bias = -0.2; - vec4 rain_pos = occlusion_texture_mat * vec4(fragPos, 1.0) - vec4(0, 0, bias, 0); + vec4 rain_pos = rain_occlusion_texture_mat * vec4(fragPos, 1.0) - vec4(0, 0, bias, 0); float visibility = textureProj(sampler2DShadow(t_directed_occlusion_maps, s_directed_occlusion_maps), rain_pos); diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index 1fc1356439..167ae6b407 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -102,9 +102,9 @@ layout(set = 0, binding = 5) uniform texture2D t_alt; layout(set = 0, binding = 6) uniform sampler s_alt; // Transforms coordinate in the range 0..WORLD_SIZE to 0..1 -vec2 wpos_to_uv(texture2D tex, sampler s, vec2 wpos) { +vec2 wpos_to_uv(vec2 wpos) { // Want: (pixel + 0.5) / W - vec2 texSize = textureSize(sampler2D(tex, s), 0); + vec2 texSize = textureSize(sampler2D(t_alt, s_alt), 0); vec2 uv_pos = (wpos + 16) / (32.0 * texSize); return vec2(uv_pos.x, /*1.0 - */uv_pos.y); } @@ -114,7 +114,7 @@ layout(set = 0, binding = 12) uniform texture2D t_weather; layout(set = 0, binding = 13) uniform sampler s_weather; vec4 sample_weather(vec2 wpos) { - return textureLod(sampler2D(t_weather, s_weather), wpos_to_uv(t_alt, s_alt, wpos), 0); + return textureLod(sampler2D(t_weather, s_weather), wpos_to_uv(wpos), 0); } float cloud_tendency_at(vec2 wpos) { diff --git a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl index 335ba92214..f0feb6891c 100644 --- a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl +++ b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl @@ -26,8 +26,8 @@ layout (std140, set = 0, binding = 14) uniform u_rain_occlusion { - mat4 rainOcclusionMatrices; - mat4 texture_mat; + mat4 rain_occlusion_matrices; + mat4 rain_occlusion_texture_mat; mat4 rel_rain_dir_mat; float integrated_rain_vel; vec3 occlusion_dummy; // Fix alignment. @@ -60,5 +60,5 @@ void main() { vec3 f_chunk_pos = vec3(v_pos_norm & 0x3Fu, (v_pos_norm >> 6) & 0x3Fu, float((v_pos_norm >> 12) & 0xFFFFu) - EXTRA_NEG_Z); vec3 f_pos = f_chunk_pos + (model_offs - focus_off.xyz); - gl_Position = rainOcclusionMatrices * vec4(f_pos, 1.0); + gl_Position = rain_occlusion_matrices * vec4(f_pos, 1.0); } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index b86d69f315..2d4ff19849 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -35,7 +35,9 @@ shaderc-from-source = ["shaderc/build-from-source"] # We don't ship egui with published release builds so a separate feature is required that excludes it. default-publish = ["singleplayer", "native-dialog", "plugins", "simd"] -default = ["default-publish", "egui-ui", "hot-reloading", "shaderc-from-source"] +# Temp for bug on current wgpu version that has access violation in vulkan when constructing egui pipeline +default-no-egui = ["default-publish", "hot-reloading", "shaderc-from-source"] +default = ["default-no-egui", "egui-ui"] [dependencies] client = {package = "veloren-client", path = "../client"} diff --git a/voxygen/src/audio/ambient.rs b/voxygen/src/audio/ambient.rs index 9e568cad71..8382a43be7 100644 --- a/voxygen/src/audio/ambient.rs +++ b/voxygen/src/audio/ambient.rs @@ -1,9 +1,6 @@ //! Handles ambient non-positional sounds use crate::{ - audio::{ - channel::{AmbientChannel, AmbientChannelTag}, - AudioFrontend, - }, + audio::{channel::AmbientChannelTag, AudioFrontend}, scene::Camera, }; use client::Client; @@ -44,97 +41,65 @@ impl AmbientMgr { client: &Client, camera: &Camera, ) { + // Checks if the ambience volume is set to zero or audio is disabled + // This prevents us from running all the following code unnecessarily + if !audio.ambience_enabled() { + return; + } let ambience_volume = audio.get_ambience_volume(); + let ambience = self.ambience.read(); // Iterate through each tag for tag in AmbientChannelTag::iter() { // If the conditions warrant creating a channel of that tag - if match tag { - AmbientChannelTag::Wind => { - AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.0 - }, - AmbientChannelTag::Rain => { - AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.1 - }, - AmbientChannelTag::Thunder => { - AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.0 - }, - AmbientChannelTag::Leaves => { - AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.1 - }, - } && audio.get_ambient_channel(tag).is_none() + if AmbientChannelTag::get_tag_volume(tag, client, camera) + > match tag { + AmbientChannelTag::Wind => 0.0, + AmbientChannelTag::Rain => 0.1, + AmbientChannelTag::Thunder => 0.0, + AmbientChannelTag::Leaves => 0.1, + } + && audio.get_ambient_channel(tag).is_none() { - // 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); - break; + audio.new_ambient_channel(tag); + } + // If a channel exists run volume code + if let Some(channel_index) = audio.get_ambient_channel_index(tag) { + let channel = &mut audio.ambient_channels[channel_index]; + + // Maintain: get the correct multiplier of whatever the tag of the current + // channel is + let target_volume = get_target_volume(tag, state, client, camera); + // Get multiplier of the current channel + let initial_volume = channel.multiplier; + + // Lerp multiplier of current channel + // TODO: Make this not framerate dependent + channel.multiplier = Lerp::lerp(initial_volume, target_volume, 0.02); + + // Update with sfx volume + channel.set_volume(ambience_volume); + + // Set the duration of the loop to whatever the current value is (0.0 by + // default) + + // If the sound should loop at this point: + if channel.began_playing.elapsed().as_secs_f32() > channel.next_track_change { + let track = ambience.tracks.iter().find(|track| track.tag == tag); + // Set the channel's start point at this instant + channel.began_playing = Instant::now(); + if let Some(track) = track { + // Set loop duration to the one specified in the ron + 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); } - } - // If the conditions don't warrant the creation of a - // channel with that tag, but a channel with - // that tag remains nonetheless, run the volume code - } else if audio.get_ambient_channel(tag).is_some() { - for index in 0..AmbientChannelTag::iter().len() { - // Update with sfx volume - audio.ambient_channels[index].set_volume(ambience_volume); - // 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 = AmbientChannel::maintain(tag, state, client, camera); - // Get multiplier of the current channel - let initial_volume = audio.ambient_channels[index].get_multiplier(); + }; - // Lerp multiplier of current channel - audio.ambient_channels[index].set_multiplier(Lerp::lerp( - initial_volume, - target_volume, - 0.02, - )); - - // 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(); - - // 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); - // Set the channel's start point at this instant - 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); - } - }; - - // 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; - } - } - } else { - // No need to run code at all, move on to the next tag - continue; + // Remove channel if not playing + if audio.ambient_channels[channel_index].multiplier <= 0.001 { + audio.ambient_channels.remove(channel_index); + }; } } } @@ -195,10 +160,12 @@ impl AmbientChannelTag { rain_intensity.min(0.9) }, AmbientChannelTag::Thunder => { - if client.weather_at_player().rain * 500.0 < 0.7 { + let rain_intensity = client.weather_at_player().rain * 500.0; + + if rain_intensity < 0.7 { 0.0 } else { - client.weather_at_player().rain * 500.0 + rain_intensity } }, AmbientChannelTag::Leaves => { @@ -229,51 +196,38 @@ impl AmbientChannelTag { } } -impl AmbientChannel { - pub fn maintain( - tag: AmbientChannelTag, - state: &State, - client: &Client, - camera: &Camera, - ) -> f32 { - let focus_off = camera.get_focus_pos().map(f32::trunc); - let cam_pos = camera.dependents().cam_pos + focus_off; +/// Checks various factors to determine the target volume to lerp to +fn get_target_volume( + tag: AmbientChannelTag, + state: &State, + 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 mut target_volume: f32 = AmbientChannelTag::get_tag_volume(tag, client, camera); + let mut volume_multiplier: f32 = AmbientChannelTag::get_tag_volume(tag, client, camera); - target_volume = AmbientChannel::check_camera(state, client, cam_pos, target_volume); - - target_volume + let terrain_alt = if let Some(chunk) = client.current_chunk() { + chunk.meta().alt() + } else { + 0.0 + }; + // Checks if the camera is underwater to diminish 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 - 20.0 { + volume_multiplier = 0.0; } - fn check_camera( - state: &State, - client: &Client, - cam_pos: Vec3, - 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 diminish 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 - 20.0 { - volume_multiplier = 0.0; - } - - volume_multiplier.clamped(0.0, 1.0) - } + volume_multiplier.clamped(0.0, 1.0) } pub fn load_ambience_items() -> AssetHandle { diff --git a/voxygen/src/audio/channel.rs b/voxygen/src/audio/channel.rs index 0f2b612f71..3d3a5642f1 100644 --- a/voxygen/src/audio/channel.rs +++ b/voxygen/src/audio/channel.rs @@ -11,7 +11,7 @@ //! [`AudioSettings`](../../settings/struct.AudioSettings.html) //! //! When the AudioFrontend's -//! [`play_sfx`](../struct.AudioFrontend.html#method.play_sfx) +//! [`emit_sfx`](../struct.AudioFrontend.html#method.emit_sfx) //! methods is called, it attempts to retrieve an SfxChannel for playback. If //! the channel capacity has been reached and all channels are occupied, a //! warning is logged, and no sound is played. @@ -171,10 +171,10 @@ pub enum AmbientChannelTag { /// which are always heard at the camera's position. pub struct AmbientChannel { tag: AmbientChannelTag, - multiplier: f32, + pub multiplier: f32, sink: Sink, - began_playing: Instant, - next_track_change: f32, + pub began_playing: Instant, + pub next_track_change: f32, } impl AmbientChannel { @@ -215,27 +215,11 @@ impl AmbientChannel { pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume * self.multiplier); } - pub fn set_multiplier(&mut self, multiplier: f32) { self.multiplier = multiplier; } - - pub fn get_volume(&mut self) -> f32 { self.sink.volume() } - - pub fn get_multiplier(&mut self) -> f32 { self.multiplier } + // pub fn get_volume(&mut self) -> f32 { self.sink.volume() } pub fn get_tag(&self) -> AmbientChannelTag { self.tag } - pub fn set_tag(&mut self, tag: AmbientChannelTag) { self.tag = tag } - - pub fn get_began_playing(&self) -> Instant { self.began_playing } - - pub fn get_next_track_change(&self) -> f32 { self.next_track_change } - - pub fn set_began_playing(&mut self, began_playing: Instant) { - self.began_playing = began_playing - } - - pub fn set_next_track_change(&mut self, next_track_change: f32) { - self.next_track_change = next_track_change - } + // pub fn set_tag(&mut self, tag: AmbientChannelTag) { self.tag = tag } } /// An SfxChannel uses a positional audio sink, and is designed for short-lived diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index dbf4e590b1..482b8d0d20 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -81,14 +81,11 @@ impl AudioFrontend { }; let mut sfx_channels = Vec::with_capacity(num_sfx_channels); - if let Some(audio_stream) = &audio_stream { - sfx_channels.resize_with(num_sfx_channels, || SfxChannel::new(audio_stream)); - }; - let mut ui_channels = Vec::with_capacity(num_ui_channels); if let Some(audio_stream) = &audio_stream { - ui_channels.resize_with(num_ui_channels, || UiChannel::new(audio_stream)) - } + ui_channels.resize_with(num_ui_channels, || UiChannel::new(audio_stream)); + sfx_channels.resize_with(num_sfx_channels, || SfxChannel::new(audio_stream)); + }; Self { // The following is for the disabled device switcher @@ -219,39 +216,6 @@ impl AudioFrontend { self.music_channels.last_mut() } - /// Function to play sfx from external places. Useful for UI and - /// inventory events - pub fn emit_sfx_item( - &mut self, - trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, - vol: Option, - ) { - 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::() % item.files.len(); - &item.files[rand_step] - }, - }; - - match self.play_ui_sfx(sfx_file, vol) { - Ok(_) => {}, - Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), - } - } else { - debug!("Missing sfx trigger config for external sfx event.",); - } - } - /// Play an sfx file given the position, SfxEvent, and whether it is /// underwater or not pub fn emit_sfx( @@ -262,6 +226,9 @@ impl AudioFrontend { 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() { 0 => { debug!("Sfx event {:?} is missing audio file.", event); @@ -277,10 +244,20 @@ impl AudioFrontend { &item.files[rand_step] }, }; + // Play sound in empty channel at given position + if self.audio_stream.is_some() { + let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); - match self.play_sfx(sfx_file, position, volume, underwater) { - Ok(_) => {}, - Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), + let listener = self.listener.clone(); + if let Some(channel) = self.get_sfx_channel() { + channel.set_pos(position); + channel.update(&listener); + if underwater { + channel.play_with_low_pass_filter(sound.convert_samples()); + } else { + channel.play(sound); + } + } } } else { debug!( @@ -290,48 +267,47 @@ impl AudioFrontend { } } - /// Play (once) an sfx file by file path at the given position and volume. - /// If `underwater` is true, the sound is played with a low pass filter - pub fn play_sfx( + /// Plays a sfx using a non-spatial sink at the given volume; doesn't need a + /// position + /// Passing no volume will default to 1.0 + pub fn emit_ui_sfx( &mut self, - sound: &str, - pos: Vec3, - vol: Option, - underwater: bool, - ) -> Result<(), rodio::decoder::DecoderError> { - if self.audio_stream.is_some() { - let sound = load_ogg(sound).amplify(vol.unwrap_or(1.0)); + trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, + volume: Option, + ) { + // 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::() % item.files.len(); + &item.files[rand_step] + }, + }; + // Play sound in empty channel + if self.audio_stream.is_some() { + let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); - let listener = self.listener.clone(); - if let Some(channel) = self.get_sfx_channel() { - channel.set_pos(pos); - channel.update(&listener); - if underwater { - channel.play_with_low_pass_filter(sound.convert_samples()); - } else { + if let Some(channel) = self.get_ui_channel() { channel.play(sound); } } + } else { + debug!("Missing sfx trigger config for external sfx event.",); } - Ok(()) } - pub fn play_ui_sfx( - &mut self, - sound: &str, - vol: Option, - ) -> Result<(), rodio::decoder::DecoderError> { - if self.audio_stream.is_some() { - let sound = load_ogg(sound).amplify(vol.unwrap_or(1.0)); - - if let Some(channel) = self.get_ui_channel() { - channel.play(sound); - } - } - Ok(()) - } - - // Plays a file at a given volume in the channel with a given tag + /// Plays a file at a given volume in the channel with a given tag fn play_ambient( &mut self, channel_tag: AmbientChannelTag, @@ -346,7 +322,7 @@ impl AudioFrontend { } } - // Adds a new ambient channel of the given tag at zero volume + /// Adds a new ambient channel of the given tag at zero volume fn new_ambient_channel(&mut self, channel_tag: AmbientChannelTag) { if let Some(audio_stream) = &self.audio_stream { let ambient_channel = AmbientChannel::new(audio_stream, channel_tag, 0.0); @@ -355,6 +331,7 @@ impl AudioFrontend { } /// Retrieves the channel currently having the given tag + /// If no channel with the given tag is found, returns None fn get_ambient_channel( &mut self, channel_tag: AmbientChannelTag, @@ -368,6 +345,19 @@ impl AudioFrontend { } } + /// Retrieves the index of the channel having the given tag in the array of + /// ambient channels This is used for times when borrowing becomes + /// difficult If no channel with the given tag is found, returns None + fn get_ambient_channel_index(&self, channel_tag: AmbientChannelTag) -> Option { + if self.audio_stream.is_some() { + self.ambient_channels + .iter() + .position(|channel| channel.get_tag() == channel_tag) + } else { + None + } + } + // Unused code that may be useful in the future: // Sets the volume of the channel with the given tag to the given volume // fn set_ambient_volume(&mut self, channel_tag: AmbientChannelTag, @@ -470,12 +460,19 @@ impl AudioFrontend { pub fn set_sfx_volume(&mut self, sfx_volume: f32) { self.sfx_volume = sfx_volume; - self.update_sfx_volumes(); + let sfx_volume = self.get_sfx_volume(); + for channel in self.sfx_channels.iter_mut() { + channel.set_volume(sfx_volume); + } + for channel in self.ui_channels.iter_mut() { + channel.set_volume(sfx_volume); + } } pub fn set_ambience_volume(&mut self, ambience_volume: f32) { self.ambience_volume = ambience_volume; + let ambience_volume = self.get_ambience_volume(); for channel in self.ambient_channels.iter_mut() { channel.set_volume(ambience_volume) } @@ -490,6 +487,7 @@ impl AudioFrontend { } } + /// Updates master volume in all channels pub fn set_master_volume(&mut self, master_volume: f32) { self.master_volume = master_volume; @@ -497,15 +495,17 @@ impl AudioFrontend { for channel in self.music_channels.iter_mut() { channel.set_volume(music_volume); } - - self.update_sfx_volumes(); - } - - fn update_sfx_volumes(&mut self) { let sfx_volume = self.get_sfx_volume(); for channel in self.sfx_channels.iter_mut() { channel.set_volume(sfx_volume); } + for channel in self.ui_channels.iter_mut() { + channel.set_volume(sfx_volume); + } + let ambience_volume = self.get_ambience_volume(); + for channel in self.ambient_channels.iter_mut() { + channel.set_volume(ambience_volume) + } } pub fn stop_ambient_sounds(&mut self) { diff --git a/voxygen/src/audio/music.rs b/voxygen/src/audio/music.rs index e56e83ea07..262e9aa4b8 100644 --- a/voxygen/src/audio/music.rs +++ b/voxygen/src/audio/music.rs @@ -228,6 +228,11 @@ impl MusicMgr { use common::comp::{group::ENEMY, Group, Health, Pos}; use specs::{Join, WorldExt}; + // Checks if the music volume is set to zero or audio is disabled + // This prevents us from running all the following code unnecessarily + if !audio.music_enabled() { + return; + } let mut activity_state = MusicActivity::Explore; diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 86378f46bf..1a45c39927 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -412,14 +412,13 @@ impl SfxMgr { outcome: &Outcome, audio: &mut AudioFrontend, client: &Client, - state: &State, underwater: bool, ) { if !audio.sfx_enabled() { return; } let triggers = self.triggers.read(); - let uids = state.ecs().read_storage::(); + let uids = client.state().ecs().read_storage::(); // TODO handle underwater match outcome { @@ -495,7 +494,7 @@ impl SfxMgr { if let Some(client_uid) = uids.get(client.entity()) { if uid == client_uid { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SkillPointGain); - audio.emit_sfx_item(sfx_trigger_item, Some(0.4)); + audio.emit_ui_sfx(sfx_trigger_item, Some(0.4)); } } }, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 10a21abbab..4379a47a01 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2386,11 +2386,11 @@ impl Hud { .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.time, ui_widgets); - + // Weather let weather = client.weather_at_player(); Text::new(&format!( - "Weather({kind:.5}): {{cloud: {cloud:.5}, rain: {rain:.5}, wind: <{wind_x:.5}, \ - {wind_y:.2}>}}", + "Weather({kind}): {{cloud: {cloud:.2}, rain: {rain:.2}, wind: <{wind_x:.0}, \ + {wind_y:.0}>}}", kind = weather.get_kind(), cloud = weather.cloud, rain = weather.rain, @@ -2521,7 +2521,7 @@ impl Hud { // Set debug box dimensions, only timings height is dynamic // TODO: Make the background box size fully dynamic - let debug_bg_size = [320.0, 370.0 + timings_height]; + let debug_bg_size = [320.0, 385.0 + timings_height]; Rectangle::fill(debug_bg_size) .rgba(0.0, 0.0, 0.0, global_state.settings.chat.chat_opacity) diff --git a/voxygen/src/render/pipelines/lod_terrain.rs b/voxygen/src/render/pipelines/lod_terrain.rs index 7cabfd4ef7..7a4a757eaf 100644 --- a/voxygen/src/render/pipelines/lod_terrain.rs +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -65,7 +65,7 @@ impl LodData { lod_base: &[u32], lod_alt: &[u32], lod_horizon: &[u32], - clouds_size: Vec2, + weather_size: Vec2, tgt_detail: u32, //border_color: gfx::texture::PackedColor, ) -> Self { @@ -139,8 +139,8 @@ impl LodData { let texture_info = wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { - width: clouds_size.x, - height: clouds_size.y, + width: weather_size.x, + height: weather_size.y, depth_or_array_layers: 1, }, mip_level_count: 1, @@ -177,7 +177,7 @@ impl LodData { &texture_info, &view_info, &sampler_info, - vec![0; clouds_size.x as usize * clouds_size.y as usize * 4].as_slice(), + vec![0; weather_size.x as usize * weather_size.y as usize * 4].as_slice(), ) }; Self { diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index a6ce260b80..3ddd7252e6 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -70,6 +70,8 @@ pub struct Globals { // To keep 16-byte-aligned. globals_dummy: [f32; 1], } +/// Make sure Globals is 16-byte-aligned. +const _: () = assert!(core::mem::size_of::() % 16 == 0); #[repr(C)] #[derive(Copy, Clone, Debug, Zeroable, Pod)] @@ -110,7 +112,6 @@ impl Globals { cam_mode: CameraMode, sprite_render_distance: f32, ) -> Self { - // dbg!(core::mem::size_of::() % 16); Self { view_mat: view_mat.into_col_arrays(), proj_mat: proj_mat.into_col_arrays(), diff --git a/voxygen/src/render/pipelines/rain_occlusion.rs b/voxygen/src/render/pipelines/rain_occlusion.rs index 2ea313955d..754972f5b6 100644 --- a/voxygen/src/render/pipelines/rain_occlusion.rs +++ b/voxygen/src/render/pipelines/rain_occlusion.rs @@ -7,25 +7,29 @@ use vek::*; #[repr(C)] #[derive(Copy, Clone, Debug, Zeroable, Pod, Default)] pub struct Locals { - shadow_matrices: [[f32; 4]; 4], - texture_mats: [[f32; 4]; 4], + rain_occlusion_matrices: [[f32; 4]; 4], + rain_occlusion_texture_mat: [[f32; 4]; 4], + /// A rotation of the direction of the rain, relative to the players + /// velocity. rel_rain_dir_mat: [[f32; 4]; 4], + /// A value to offset the rain, to make it move over time. integrated_rain_vel: f32, // To keep 16-byte-aligned. occlusion_dummy: [f32; 3], } +/// Make sure Locals is 16-byte-aligned. +const _: () = assert!(core::mem::size_of::() % 16 == 0); impl Locals { pub fn new( - shadow_mat: Mat4, - texture_mat: Mat4, + rain_occlusion_matrices: Mat4, + rain_occlusion_texture_mat: Mat4, rel_rain_dir_mat: Mat4, integrated_rain_vel: f32, ) -> Self { - // dbg!(core::mem::size_of::() % 16); Self { - shadow_matrices: shadow_mat.into_col_arrays(), - texture_mats: texture_mat.into_col_arrays(), + rain_occlusion_matrices: rain_occlusion_matrices.into_col_arrays(), + rain_occlusion_texture_mat: rain_occlusion_texture_mat.into_col_arrays(), rel_rain_dir_mat: rel_rain_dir_mat.into_col_arrays(), integrated_rain_vel, occlusion_dummy: [0.0; 3], @@ -91,7 +95,7 @@ impl RainOcclusionFigurePipeline { let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Directed figure shadow pipeline layout"), + label: Some("Rain occlusion pipeline layout"), push_constant_ranges: &[], bind_group_layouts: &[&global_layout.globals, &figure_layout.locals], }); @@ -104,7 +108,7 @@ impl RainOcclusionFigurePipeline { }; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Directed shadow figure pipeline"), + label: Some("Rain occlusion figure pipeline"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { module: vs_module, diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 34a48b5f9f..156cafb83d 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -49,10 +49,6 @@ pub type ColLightInfo = (Vec<[u8; 4]>, Vec2); const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; -// For rain occlusion we only need to render the closest chunks. -/// How many chunks are maximally rendered for rain occlusion. -pub const RAIN_OCCLUSION_CHUNKS: usize = 9; - /// A type that stores all the layouts associated with this renderer that never /// change when the RenderMode is modified. struct ImmutableLayouts { @@ -738,10 +734,8 @@ impl Renderer { update_shadow_bind = true; }, - shadow => { - if let Err(err) = shadow { - warn!("Could not create shadow map views: {:?}", err); - } + Err(err) => { + warn!("Could not create shadow map views: {:?}", err); }, } } @@ -755,10 +749,8 @@ impl Renderer { update_shadow_bind = true; }, - rain => { - if let Err(err) = rain { - warn!("Could not create rain occlusion map view: {:?}", err); - } + Err(err) => { + warn!("Could not create rain occlusion map view: {:?}", err); }, } } diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 7fdedd371d..b1fb5e1e88 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -136,8 +136,8 @@ impl<'frame> Drawer<'frame> { /// Get the pipeline modes. pub fn pipeline_modes(&self) -> &super::super::PipelineModes { self.borrow.pipeline_modes } - /// Returns None if the shadow renderer is not enabled at some level or the - /// pipelines are not available yet + /// Returns None if the rain occlusion renderer is not enabled at some + /// level, the pipelines are not available yet or clouds are disabled. pub fn rain_occlusion_pass(&mut self) -> Option { if !self.borrow.pipeline_modes.cloud.is_enabled() { return None; @@ -663,7 +663,7 @@ impl<'pass> ShadowPassDrawer<'pass> { pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> { let mut render_pass = self .render_pass - .scope("direcred_figure_shadows", self.borrow.device); + .scope("directed_figure_shadows", self.borrow.device); render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline); set_quad_index_buffer::(&mut render_pass, self.borrow); @@ -674,7 +674,7 @@ impl<'pass> ShadowPassDrawer<'pass> { pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> { let mut render_pass = self .render_pass - .scope("direcred_terrain_shadows", self.borrow.device); + .scope("directed_terrain_shadows", self.borrow.device); render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline); set_quad_index_buffer::(&mut render_pass, self.borrow); @@ -694,7 +694,7 @@ impl<'pass> RainOcclusionPassDrawer<'pass> { pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> { let mut render_pass = self .render_pass - .scope("direcred_figure_rain_occlusion", self.borrow.device); + .scope("directed_figure_rain_occlusion", self.borrow.device); render_pass.set_pipeline(&self.rain_occlusion_renderer.figure_pipeline.pipeline); set_quad_index_buffer::(&mut render_pass, self.borrow); @@ -705,7 +705,7 @@ impl<'pass> RainOcclusionPassDrawer<'pass> { pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> { let mut render_pass = self .render_pass - .scope("direcred_terrain_rain_occlusion", self.borrow.device); + .scope("directed_terrain_rain_occlusion", self.borrow.device); render_pass.set_pipeline(&self.rain_occlusion_renderer.terrain_pipeline.pipeline); set_quad_index_buffer::(&mut render_pass, self.borrow); diff --git a/voxygen/src/render/renderer/rain_occlusion_map.rs b/voxygen/src/render/renderer/rain_occlusion_map.rs index 3fb37b2a5e..124deb4a72 100644 --- a/voxygen/src/render/renderer/rain_occlusion_map.rs +++ b/voxygen/src/render/renderer/rain_occlusion_map.rs @@ -1,4 +1,4 @@ -use crate::render::{pipelines::rain_occlusion, renderer::RAIN_OCCLUSION_CHUNKS}; +use crate::{render::pipelines::rain_occlusion, scene::terrain::RAIN_OCCLUSION_CHUNKS}; use super::{ super::{texture::Texture, RenderError, ShadowMapMode}, @@ -7,8 +7,8 @@ use super::{ use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use vek::*; -/// A type that holds shadow map data. Since shadow mapping may not be -/// supported on all platforms, we try to keep it separate. +/// A type that holds rain occlusion map data. Since rain occlusion mapping may +/// not be supported on all platforms, we try to keep it separate. pub struct RainOcclusionMapRenderer { pub depth: Texture, diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 01fd3e66aa..9e870e28a6 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -420,7 +420,7 @@ impl Scene { .unwrap_or(false); self.particle_mgr.handle_outcome(outcome, scene_data); self.sfx_mgr - .handle_outcome(outcome, audio, scene_data.client, state, underwater); + .handle_outcome(outcome, audio, scene_data.client, underwater); match outcome { Outcome::Explosion { @@ -1022,6 +1022,8 @@ impl Scene { ); renderer.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]); + } else { + self.integrated_rain_vel = 0.0; } let sun_dir = scene_data.get_sun_dir(); @@ -1106,25 +1108,19 @@ impl Scene { self.figure_mgr.clean(scene_data.tick); // Maintain audio - if audio.sfx_enabled() { - self.sfx_mgr.maintain( - audio, - scene_data.state, - scene_data.player_entity, - &self.camera, - &self.terrain, - client, - ); - } + self.sfx_mgr.maintain( + audio, + scene_data.state, + scene_data.player_entity, + &self.camera, + &self.terrain, + client, + ); - if audio.ambience_enabled() { - self.ambient_mgr - .maintain(audio, scene_data.state, client, &self.camera); - } + self.ambient_mgr + .maintain(audio, scene_data.state, client, &self.camera); - if audio.music_enabled() { - self.music_mgr.maintain(audio, scene_data.state, client); - } + self.music_mgr.maintain(audio, scene_data.state, client); } pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group } @@ -1180,7 +1176,7 @@ impl Scene { prof_span!("rain occlusion"); if let Some(mut occlusion_pass) = drawer.rain_occlusion_pass() { self.terrain - .render_occlusion(&mut occlusion_pass.draw_terrain_shadows(), cam_pos); + .render_rain_occlusion(&mut occlusion_pass.draw_terrain_shadows(), cam_pos); self.figure_mgr.render_shadows( &mut occlusion_pass.draw_figure_shadows(), diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 5d61660dee..5c24f924ab 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -10,7 +10,6 @@ use crate::{ }, render::{ pipelines::{self, ColLights}, - renderer::RAIN_OCCLUSION_CHUNKS, ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, @@ -47,6 +46,10 @@ use vek::*; const SPRITE_SCALE: Vec3 = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); const SPRITE_LOD_LEVELS: usize = 5; +// For rain occlusion we only need to render the closest chunks. +/// How many chunks are maximally rendered for rain occlusion. +pub const RAIN_OCCLUSION_CHUNKS: usize = 9; + #[derive(Clone, Copy, Debug)] struct Visibility { in_range: bool, @@ -1405,10 +1408,9 @@ impl Terrain { // Check if there is rain near the camera let max_weather = scene_data.state.max_weather_near(focus_pos.xy()); let visible_occlusion_volume = if max_weather.rain > 0.0 { - let occlusion_box = visible_bounding_box; let visible_bounding_box = math::Aabb:: { - min: math::Vec3::from(occlusion_box.min - focus_off), - max: math::Vec3::from(occlusion_box.max - focus_off), + min: math::Vec3::from(visible_bounding_box.min - focus_off), + max: math::Vec3::from(visible_bounding_box.max - focus_off), }; let visible_bounds_fine = math::Aabb { min: visible_bounding_box.min.as_::(), @@ -1490,7 +1492,7 @@ impl Terrain { .for_each(|(model, locals)| drawer.draw(model, locals)); } - pub fn render_occlusion<'a>( + pub fn render_rain_occlusion<'a>( &'a self, drawer: &mut TerrainShadowDrawer<'_, 'a>, focus_pos: Vec3, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 6a521c7d67..7a3ccf5add 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -269,9 +269,7 @@ impl SessionState { | InventoryUpdateEvent::EntityCollectFailed { .. } | InventoryUpdateEvent::BlockCollectFailed { .. } | InventoryUpdateEvent::Craft => { - global_state - .audio - .emit_sfx_item(sfx_trigger_item, Some(1.0)); + global_state.audio.emit_ui_sfx(sfx_trigger_item, Some(1.0)); }, _ => global_state.audio.emit_sfx( sfx_trigger_item, @@ -1207,6 +1205,9 @@ impl PlayState for SessionState { }, HudEvent::Logout => { self.client.borrow_mut().logout(); + // 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_sfx(); return PlayStateResult::Pop;