Clean up audio code, fix egui bug

This commit is contained in:
DaforLynx 2022-06-03 18:25:17 -07:00 committed by IsseW
parent 72a0f56788
commit ac82689f83
23 changed files with 295 additions and 352 deletions

BIN
assets/voxygen/audio/ambience/leaves.ogg (Stored with Git LFS)

Binary file not shown.

View File

@ -17,7 +17,7 @@
), ),
( (
path:"voxygen.audio.ambience.leaves", path:"voxygen.audio.ambience.leaves",
length: 27.0, length: 26.0,
tag: Leaves, tag: Leaves,
), ),
] ]

View File

@ -108,44 +108,47 @@ void main() {
vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z); vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z);
vec3 cam_wpos = cam_pos.xyz + focus_off.xyz; vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
float rain_dist = 50.0; float rain = rain_density_at(cam_wpos.xy);
for (int i = 0; i < 4; i ++) { if (rain > 0.0) {
float old_rain_dist = rain_dist; float rain_dist = 50.0;
rain_dist *= 0.3; 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); vec2 rain_pos = (view_pos * rain_dist);
rain_pos.y += integrated_rain_vel; 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( float drop_depth = mix(
old_rain_dist, old_rain_dist,
rain_dist, rain_dist,
fract(hash(fract(vec4(cell, rain_dist, 0) * 0.1))) fract(hash(fract(vec4(cell, rain_dist, 0) * 0.1)))
); );
vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * drop_depth; vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * drop_depth;
float dist_to_rain = length(rpos); float dist_to_rain = length(rpos);
if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) { if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) {
continue; 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); tgt_color = vec4(color.rgb, 1);

View File

@ -125,7 +125,7 @@ vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) {
// Gets the altitude at a position relative to focus_off. // Gets the altitude at a position relative to focus_off.
float alt_at(vec2 pos) { 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); 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; //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0;

View File

@ -10,8 +10,8 @@ uniform samplerShadow s_directed_occlusion_maps;
layout (std140, set = 0, binding = 14) layout (std140, set = 0, binding = 14)
uniform u_rain_occlusion { uniform u_rain_occlusion {
mat4 occlusionMatrices; mat4 rain_occlusion_matrices;
mat4 occlusion_texture_mat; mat4 rain_occlusion_texture_mat;
mat4 rel_rain_dir_mat; mat4 rel_rain_dir_mat;
float integrated_rain_vel; float integrated_rain_vel;
vec3 occlusion_dummy; // Fix alignment. vec3 occlusion_dummy; // Fix alignment.
@ -21,7 +21,7 @@ float rain_occlusion_at(in vec3 fragPos)
{ {
float bias = -0.2; 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); float visibility = textureProj(sampler2DShadow(t_directed_occlusion_maps, s_directed_occlusion_maps), rain_pos);

View File

@ -102,9 +102,9 @@ layout(set = 0, binding = 5) uniform texture2D t_alt;
layout(set = 0, binding = 6) uniform sampler s_alt; layout(set = 0, binding = 6) uniform sampler s_alt;
// Transforms coordinate in the range 0..WORLD_SIZE to 0..1 // 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 // 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); vec2 uv_pos = (wpos + 16) / (32.0 * texSize);
return vec2(uv_pos.x, /*1.0 - */uv_pos.y); 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; layout(set = 0, binding = 13) uniform sampler s_weather;
vec4 sample_weather(vec2 wpos) { 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) { float cloud_tendency_at(vec2 wpos) {

View File

@ -26,8 +26,8 @@
layout (std140, set = 0, binding = 14) layout (std140, set = 0, binding = 14)
uniform u_rain_occlusion { uniform u_rain_occlusion {
mat4 rainOcclusionMatrices; mat4 rain_occlusion_matrices;
mat4 texture_mat; mat4 rain_occlusion_texture_mat;
mat4 rel_rain_dir_mat; mat4 rel_rain_dir_mat;
float integrated_rain_vel; float integrated_rain_vel;
vec3 occlusion_dummy; // Fix alignment. 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_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); 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);
} }

View File

@ -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. # 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-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] [dependencies]
client = {package = "veloren-client", path = "../client"} client = {package = "veloren-client", path = "../client"}

View File

@ -1,9 +1,6 @@
//! Handles ambient non-positional sounds //! Handles ambient non-positional sounds
use crate::{ use crate::{
audio::{ audio::{channel::AmbientChannelTag, AudioFrontend},
channel::{AmbientChannel, AmbientChannelTag},
AudioFrontend,
},
scene::Camera, scene::Camera,
}; };
use client::Client; use client::Client;
@ -44,97 +41,65 @@ impl AmbientMgr {
client: &Client, client: &Client,
camera: &Camera, 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_volume = audio.get_ambience_volume();
let ambience = self.ambience.read();
// Iterate through each tag // Iterate through each tag
for tag in AmbientChannelTag::iter() { for tag in AmbientChannelTag::iter() {
// If the conditions warrant creating a channel of that tag // If the conditions warrant creating a channel of that tag
if match tag { if AmbientChannelTag::get_tag_volume(tag, client, camera)
AmbientChannelTag::Wind => { > match tag {
AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.0 AmbientChannelTag::Wind => 0.0,
}, AmbientChannelTag::Rain => 0.1,
AmbientChannelTag::Rain => { AmbientChannelTag::Thunder => 0.0,
AmbientChannelTag::get_tag_volume(tag, client, camera) > 0.1 AmbientChannelTag::Leaves => 0.1,
}, }
AmbientChannelTag::Thunder => { && audio.get_ambient_channel(tag).is_none()
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()
{ {
// Iterate through the supposed number of channels - one for each tag audio.new_ambient_channel(tag);
for index in 0..AmbientChannelTag::iter().len() { }
// If index would exceed current number of channels, create a new one with // If a channel exists run volume code
// current tag if let Some(channel_index) = audio.get_ambient_channel_index(tag) {
if index >= audio.ambient_channels.len() { let channel = &mut audio.ambient_channels[channel_index];
audio.new_ambient_channel(tag);
break; // 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 // Remove channel if not playing
audio.ambient_channels[index].set_multiplier(Lerp::lerp( if audio.ambient_channels[channel_index].multiplier <= 0.001 {
initial_volume, audio.ambient_channels.remove(channel_index);
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;
} }
} }
} }
@ -195,10 +160,12 @@ impl AmbientChannelTag {
rain_intensity.min(0.9) rain_intensity.min(0.9)
}, },
AmbientChannelTag::Thunder => { 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 0.0
} else { } else {
client.weather_at_player().rain * 500.0 rain_intensity
} }
}, },
AmbientChannelTag::Leaves => { AmbientChannelTag::Leaves => {
@ -229,51 +196,38 @@ impl AmbientChannelTag {
} }
} }
impl AmbientChannel { /// Checks various factors to determine the target volume to lerp to
pub fn maintain( fn get_target_volume(
tag: AmbientChannelTag, tag: AmbientChannelTag,
state: &State, state: &State,
client: &Client, client: &Client,
camera: &Camera, camera: &Camera,
) -> f32 { ) -> f32 {
let focus_off = camera.get_focus_pos().map(f32::trunc); let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off; 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); let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
target_volume } 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( volume_multiplier.clamped(0.0, 1.0)
state: &State,
client: &Client,
cam_pos: Vec3<f32>,
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)
}
} }
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> { pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {

View File

@ -11,7 +11,7 @@
//! [`AudioSettings`](../../settings/struct.AudioSettings.html) //! [`AudioSettings`](../../settings/struct.AudioSettings.html)
//! //!
//! When the AudioFrontend's //! 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 //! methods is called, it attempts to retrieve an SfxChannel for playback. If
//! the channel capacity has been reached and all channels are occupied, a //! the channel capacity has been reached and all channels are occupied, a
//! warning is logged, and no sound is played. //! warning is logged, and no sound is played.
@ -171,10 +171,10 @@ pub enum AmbientChannelTag {
/// which are always heard at the camera's position. /// which are always heard at the camera's position.
pub struct AmbientChannel { pub struct AmbientChannel {
tag: AmbientChannelTag, tag: AmbientChannelTag,
multiplier: f32, pub multiplier: f32,
sink: Sink, sink: Sink,
began_playing: Instant, pub began_playing: Instant,
next_track_change: f32, pub next_track_change: f32,
} }
impl AmbientChannel { 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_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_volume(&mut self) -> f32 { self.sink.volume() }
pub fn get_multiplier(&mut self) -> f32 { self.multiplier }
pub fn get_tag(&self) -> AmbientChannelTag { self.tag } pub fn get_tag(&self) -> AmbientChannelTag { self.tag }
pub fn set_tag(&mut self, tag: AmbientChannelTag) { self.tag = 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
}
} }
/// An SfxChannel uses a positional audio sink, and is designed for short-lived /// An SfxChannel uses a positional audio sink, and is designed for short-lived

View File

@ -81,14 +81,11 @@ impl AudioFrontend {
}; };
let mut sfx_channels = Vec::with_capacity(num_sfx_channels); 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); let mut ui_channels = Vec::with_capacity(num_ui_channels);
if let Some(audio_stream) = &audio_stream { 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 { Self {
// The following is for the disabled device switcher // The following is for the disabled device switcher
@ -219,39 +216,6 @@ impl AudioFrontend {
self.music_channels.last_mut() 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<f32>,
) {
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]
},
};
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 /// Play an sfx file given the position, SfxEvent, and whether it is
/// underwater or not /// underwater or not
pub fn emit_sfx( pub fn emit_sfx(
@ -262,6 +226,9 @@ impl AudioFrontend {
underwater: bool, underwater: bool,
) { ) {
if let Some((event, item)) = trigger_item { 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() { let sfx_file = match item.files.len() {
0 => { 0 => {
debug!("Sfx event {:?} is missing audio file.", event); debug!("Sfx event {:?} is missing audio file.", event);
@ -277,10 +244,20 @@ impl AudioFrontend {
&item.files[rand_step] &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) { let listener = self.listener.clone();
Ok(_) => {}, if let Some(channel) = self.get_sfx_channel() {
Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), channel.set_pos(position);
channel.update(&listener);
if underwater {
channel.play_with_low_pass_filter(sound.convert_samples());
} else {
channel.play(sound);
}
}
} }
} else { } else {
debug!( debug!(
@ -290,48 +267,47 @@ impl AudioFrontend {
} }
} }
/// Play (once) an sfx file by file path at the given position and volume. /// Plays a sfx using a non-spatial sink at the given volume; doesn't need a
/// If `underwater` is true, the sound is played with a low pass filter /// position
pub fn play_sfx( /// Passing no volume will default to 1.0
pub fn emit_ui_sfx(
&mut self, &mut self,
sound: &str, trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
pos: Vec3<f32>, volume: Option<f32>,
vol: Option<f32>, ) {
underwater: bool, // Find sound based on given trigger_item
) -> Result<(), rodio::decoder::DecoderError> { // Randomizes if multiple sounds are found
if self.audio_stream.is_some() { // Errors if no sounds are found
let sound = load_ogg(sound).amplify(vol.unwrap_or(1.0)); 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]
},
};
// 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_ui_channel() {
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 {
channel.play(sound); channel.play(sound);
} }
} }
} else {
debug!("Missing sfx trigger config for external sfx event.",);
} }
Ok(())
} }
pub fn play_ui_sfx( /// Plays a file at a given volume in the channel with a given tag
&mut self,
sound: &str,
vol: Option<f32>,
) -> 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
fn play_ambient( fn play_ambient(
&mut self, &mut self,
channel_tag: AmbientChannelTag, 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) { fn new_ambient_channel(&mut self, channel_tag: AmbientChannelTag) {
if let Some(audio_stream) = &self.audio_stream { if let Some(audio_stream) = &self.audio_stream {
let ambient_channel = AmbientChannel::new(audio_stream, channel_tag, 0.0); 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 /// Retrieves the channel currently having the given tag
/// If no channel with the given tag is found, returns None
fn get_ambient_channel( fn get_ambient_channel(
&mut self, &mut self,
channel_tag: AmbientChannelTag, 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<usize> {
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: // Unused code that may be useful in the future:
// Sets the volume of the channel with the given tag to the given volume // Sets the volume of the channel with the given tag to the given volume
// fn set_ambient_volume(&mut self, channel_tag: AmbientChannelTag, // 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) { pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
self.sfx_volume = sfx_volume; 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) { pub fn set_ambience_volume(&mut self, ambience_volume: f32) {
self.ambience_volume = ambience_volume; self.ambience_volume = ambience_volume;
let ambience_volume = self.get_ambience_volume();
for channel in self.ambient_channels.iter_mut() { for channel in self.ambient_channels.iter_mut() {
channel.set_volume(ambience_volume) 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) { pub fn set_master_volume(&mut self, master_volume: f32) {
self.master_volume = master_volume; self.master_volume = master_volume;
@ -497,15 +495,17 @@ impl AudioFrontend {
for channel in self.music_channels.iter_mut() { for channel in self.music_channels.iter_mut() {
channel.set_volume(music_volume); channel.set_volume(music_volume);
} }
self.update_sfx_volumes();
}
fn update_sfx_volumes(&mut self) {
let sfx_volume = self.get_sfx_volume(); let sfx_volume = self.get_sfx_volume();
for channel in self.sfx_channels.iter_mut() { for channel in self.sfx_channels.iter_mut() {
channel.set_volume(sfx_volume); 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) { pub fn stop_ambient_sounds(&mut self) {

View File

@ -228,6 +228,11 @@ impl MusicMgr {
use common::comp::{group::ENEMY, Group, Health, Pos}; use common::comp::{group::ENEMY, Group, Health, Pos};
use specs::{Join, WorldExt}; 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; let mut activity_state = MusicActivity::Explore;

View File

@ -412,14 +412,13 @@ impl SfxMgr {
outcome: &Outcome, outcome: &Outcome,
audio: &mut AudioFrontend, audio: &mut AudioFrontend,
client: &Client, client: &Client,
state: &State,
underwater: bool, underwater: bool,
) { ) {
if !audio.sfx_enabled() { if !audio.sfx_enabled() {
return; return;
} }
let triggers = self.triggers.read(); let triggers = self.triggers.read();
let uids = state.ecs().read_storage::<Uid>(); let uids = client.state().ecs().read_storage::<Uid>();
// TODO handle underwater // TODO handle underwater
match outcome { match outcome {
@ -495,7 +494,7 @@ impl SfxMgr {
if let Some(client_uid) = uids.get(client.entity()) { if let Some(client_uid) = uids.get(client.entity()) {
if uid == client_uid { if uid == client_uid {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SkillPointGain); 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));
} }
} }
}, },

View File

@ -2386,11 +2386,11 @@ impl Hud {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.set(self.ids.time, ui_widgets); .set(self.ids.time, ui_widgets);
// Weather
let weather = client.weather_at_player(); let weather = client.weather_at_player();
Text::new(&format!( Text::new(&format!(
"Weather({kind:.5}): {{cloud: {cloud:.5}, rain: {rain:.5}, wind: <{wind_x:.5}, \ "Weather({kind}): {{cloud: {cloud:.2}, rain: {rain:.2}, wind: <{wind_x:.0}, \
{wind_y:.2}>}}", {wind_y:.0}>}}",
kind = weather.get_kind(), kind = weather.get_kind(),
cloud = weather.cloud, cloud = weather.cloud,
rain = weather.rain, rain = weather.rain,
@ -2521,7 +2521,7 @@ impl Hud {
// Set debug box dimensions, only timings height is dynamic // Set debug box dimensions, only timings height is dynamic
// TODO: Make the background box size fully 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) Rectangle::fill(debug_bg_size)
.rgba(0.0, 0.0, 0.0, global_state.settings.chat.chat_opacity) .rgba(0.0, 0.0, 0.0, global_state.settings.chat.chat_opacity)

View File

@ -65,7 +65,7 @@ impl LodData {
lod_base: &[u32], lod_base: &[u32],
lod_alt: &[u32], lod_alt: &[u32],
lod_horizon: &[u32], lod_horizon: &[u32],
clouds_size: Vec2<u32>, weather_size: Vec2<u32>,
tgt_detail: u32, tgt_detail: u32,
//border_color: gfx::texture::PackedColor, //border_color: gfx::texture::PackedColor,
) -> Self { ) -> Self {
@ -139,8 +139,8 @@ impl LodData {
let texture_info = wgpu::TextureDescriptor { let texture_info = wgpu::TextureDescriptor {
label: None, label: None,
size: wgpu::Extent3d { size: wgpu::Extent3d {
width: clouds_size.x, width: weather_size.x,
height: clouds_size.y, height: weather_size.y,
depth_or_array_layers: 1, depth_or_array_layers: 1,
}, },
mip_level_count: 1, mip_level_count: 1,
@ -177,7 +177,7 @@ impl LodData {
&texture_info, &texture_info,
&view_info, &view_info,
&sampler_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 { Self {

View File

@ -70,6 +70,8 @@ pub struct Globals {
// To keep 16-byte-aligned. // To keep 16-byte-aligned.
globals_dummy: [f32; 1], globals_dummy: [f32; 1],
} }
/// Make sure Globals is 16-byte-aligned.
const _: () = assert!(core::mem::size_of::<Globals>() % 16 == 0);
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
@ -110,7 +112,6 @@ impl Globals {
cam_mode: CameraMode, cam_mode: CameraMode,
sprite_render_distance: f32, sprite_render_distance: f32,
) -> Self { ) -> Self {
// dbg!(core::mem::size_of::<Self>() % 16);
Self { Self {
view_mat: view_mat.into_col_arrays(), view_mat: view_mat.into_col_arrays(),
proj_mat: proj_mat.into_col_arrays(), proj_mat: proj_mat.into_col_arrays(),

View File

@ -7,25 +7,29 @@ use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod, Default)] #[derive(Copy, Clone, Debug, Zeroable, Pod, Default)]
pub struct Locals { pub struct Locals {
shadow_matrices: [[f32; 4]; 4], rain_occlusion_matrices: [[f32; 4]; 4],
texture_mats: [[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], rel_rain_dir_mat: [[f32; 4]; 4],
/// A value to offset the rain, to make it move over time.
integrated_rain_vel: f32, integrated_rain_vel: f32,
// To keep 16-byte-aligned. // To keep 16-byte-aligned.
occlusion_dummy: [f32; 3], occlusion_dummy: [f32; 3],
} }
/// Make sure Locals is 16-byte-aligned.
const _: () = assert!(core::mem::size_of::<Locals>() % 16 == 0);
impl Locals { impl Locals {
pub fn new( pub fn new(
shadow_mat: Mat4<f32>, rain_occlusion_matrices: Mat4<f32>,
texture_mat: Mat4<f32>, rain_occlusion_texture_mat: Mat4<f32>,
rel_rain_dir_mat: Mat4<f32>, rel_rain_dir_mat: Mat4<f32>,
integrated_rain_vel: f32, integrated_rain_vel: f32,
) -> Self { ) -> Self {
// dbg!(core::mem::size_of::<Self>() % 16);
Self { Self {
shadow_matrices: shadow_mat.into_col_arrays(), rain_occlusion_matrices: rain_occlusion_matrices.into_col_arrays(),
texture_mats: texture_mat.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(), rel_rain_dir_mat: rel_rain_dir_mat.into_col_arrays(),
integrated_rain_vel, integrated_rain_vel,
occlusion_dummy: [0.0; 3], occlusion_dummy: [0.0; 3],
@ -91,7 +95,7 @@ impl RainOcclusionFigurePipeline {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Directed figure shadow pipeline layout"), label: Some("Rain occlusion pipeline layout"),
push_constant_ranges: &[], push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &figure_layout.locals], bind_group_layouts: &[&global_layout.globals, &figure_layout.locals],
}); });
@ -104,7 +108,7 @@ impl RainOcclusionFigurePipeline {
}; };
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 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), layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: vs_module, module: vs_module,

View File

@ -49,10 +49,6 @@ pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u16>);
const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000;
const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 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 /// A type that stores all the layouts associated with this renderer that never
/// change when the RenderMode is modified. /// change when the RenderMode is modified.
struct ImmutableLayouts { struct ImmutableLayouts {
@ -738,10 +734,8 @@ impl Renderer {
update_shadow_bind = true; update_shadow_bind = true;
}, },
shadow => { Err(err) => {
if let Err(err) = shadow { warn!("Could not create shadow map views: {:?}", err);
warn!("Could not create shadow map views: {:?}", err);
}
}, },
} }
} }
@ -755,10 +749,8 @@ impl Renderer {
update_shadow_bind = true; update_shadow_bind = true;
}, },
rain => { Err(err) => {
if let Err(err) = rain { warn!("Could not create rain occlusion map view: {:?}", err);
warn!("Could not create rain occlusion map view: {:?}", err);
}
}, },
} }
} }

View File

@ -136,8 +136,8 @@ impl<'frame> Drawer<'frame> {
/// Get the pipeline modes. /// Get the pipeline modes.
pub fn pipeline_modes(&self) -> &super::super::PipelineModes { self.borrow.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 /// Returns None if the rain occlusion renderer is not enabled at some
/// pipelines are not available yet /// level, the pipelines are not available yet or clouds are disabled.
pub fn rain_occlusion_pass(&mut self) -> Option<RainOcclusionPassDrawer> { pub fn rain_occlusion_pass(&mut self) -> Option<RainOcclusionPassDrawer> {
if !self.borrow.pipeline_modes.cloud.is_enabled() { if !self.borrow.pipeline_modes.cloud.is_enabled() {
return None; return None;
@ -663,7 +663,7 @@ impl<'pass> ShadowPassDrawer<'pass> {
pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> { pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
let mut render_pass = self let mut render_pass = self
.render_pass .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); render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow); set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
@ -674,7 +674,7 @@ impl<'pass> ShadowPassDrawer<'pass> {
pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> { pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
let mut render_pass = self let mut render_pass = self
.render_pass .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); render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow); set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
@ -694,7 +694,7 @@ impl<'pass> RainOcclusionPassDrawer<'pass> {
pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> { pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
let mut render_pass = self let mut render_pass = self
.render_pass .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); render_pass.set_pipeline(&self.rain_occlusion_renderer.figure_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow); set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
@ -705,7 +705,7 @@ impl<'pass> RainOcclusionPassDrawer<'pass> {
pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> { pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
let mut render_pass = self let mut render_pass = self
.render_pass .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); render_pass.set_pipeline(&self.rain_occlusion_renderer.terrain_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow); set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);

View File

@ -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::{ use super::{
super::{texture::Texture, RenderError, ShadowMapMode}, super::{texture::Texture, RenderError, ShadowMapMode},
@ -7,8 +7,8 @@ use super::{
use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use common::{terrain::TerrainChunkSize, vol::RectVolSize};
use vek::*; use vek::*;
/// A type that holds shadow map data. Since shadow mapping may not be /// A type that holds rain occlusion map data. Since rain occlusion mapping may
/// supported on all platforms, we try to keep it separate. /// not be supported on all platforms, we try to keep it separate.
pub struct RainOcclusionMapRenderer { pub struct RainOcclusionMapRenderer {
pub depth: Texture, pub depth: Texture,

View File

@ -420,7 +420,7 @@ impl Scene {
.unwrap_or(false); .unwrap_or(false);
self.particle_mgr.handle_outcome(outcome, scene_data); self.particle_mgr.handle_outcome(outcome, scene_data);
self.sfx_mgr self.sfx_mgr
.handle_outcome(outcome, audio, scene_data.client, state, underwater); .handle_outcome(outcome, audio, scene_data.client, underwater);
match outcome { match outcome {
Outcome::Explosion { Outcome::Explosion {
@ -1022,6 +1022,8 @@ impl Scene {
); );
renderer.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]); 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(); let sun_dir = scene_data.get_sun_dir();
@ -1106,25 +1108,19 @@ impl Scene {
self.figure_mgr.clean(scene_data.tick); self.figure_mgr.clean(scene_data.tick);
// Maintain audio // Maintain audio
if audio.sfx_enabled() { self.sfx_mgr.maintain(
self.sfx_mgr.maintain( audio,
audio, scene_data.state,
scene_data.state, scene_data.player_entity,
scene_data.player_entity, &self.camera,
&self.camera, &self.terrain,
&self.terrain, client,
client, );
);
}
if audio.ambience_enabled() { self.ambient_mgr
self.ambient_mgr .maintain(audio, scene_data.state, client, &self.camera);
.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 } pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group }
@ -1180,7 +1176,7 @@ impl Scene {
prof_span!("rain occlusion"); prof_span!("rain occlusion");
if let Some(mut occlusion_pass) = drawer.rain_occlusion_pass() { if let Some(mut occlusion_pass) = drawer.rain_occlusion_pass() {
self.terrain 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( self.figure_mgr.render_shadows(
&mut occlusion_pass.draw_figure_shadows(), &mut occlusion_pass.draw_figure_shadows(),

View File

@ -10,7 +10,6 @@ use crate::{
}, },
render::{ render::{
pipelines::{self, ColLights}, pipelines::{self, ColLights},
renderer::RAIN_OCCLUSION_CHUNKS,
ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model, ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model,
RenderError, Renderer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, RenderError, Renderer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts,
TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE,
@ -47,6 +46,10 @@ use vek::*;
const SPRITE_SCALE: Vec3<f32> = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); const SPRITE_SCALE: Vec3<f32> = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0);
const SPRITE_LOD_LEVELS: usize = 5; 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)] #[derive(Clone, Copy, Debug)]
struct Visibility { struct Visibility {
in_range: bool, in_range: bool,
@ -1405,10 +1408,9 @@ impl<V: RectRasterableVol> Terrain<V> {
// Check if there is rain near the camera // Check if there is rain near the camera
let max_weather = scene_data.state.max_weather_near(focus_pos.xy()); let max_weather = scene_data.state.max_weather_near(focus_pos.xy());
let visible_occlusion_volume = if max_weather.rain > 0.0 { let visible_occlusion_volume = if max_weather.rain > 0.0 {
let occlusion_box = visible_bounding_box;
let visible_bounding_box = math::Aabb::<f32> { let visible_bounding_box = math::Aabb::<f32> {
min: math::Vec3::from(occlusion_box.min - focus_off), min: math::Vec3::from(visible_bounding_box.min - focus_off),
max: math::Vec3::from(occlusion_box.max - focus_off), max: math::Vec3::from(visible_bounding_box.max - focus_off),
}; };
let visible_bounds_fine = math::Aabb { let visible_bounds_fine = math::Aabb {
min: visible_bounding_box.min.as_::<f64>(), min: visible_bounding_box.min.as_::<f64>(),
@ -1490,7 +1492,7 @@ impl<V: RectRasterableVol> Terrain<V> {
.for_each(|(model, locals)| drawer.draw(model, locals)); .for_each(|(model, locals)| drawer.draw(model, locals));
} }
pub fn render_occlusion<'a>( pub fn render_rain_occlusion<'a>(
&'a self, &'a self,
drawer: &mut TerrainShadowDrawer<'_, 'a>, drawer: &mut TerrainShadowDrawer<'_, 'a>,
focus_pos: Vec3<f32>, focus_pos: Vec3<f32>,

View File

@ -269,9 +269,7 @@ impl SessionState {
| InventoryUpdateEvent::EntityCollectFailed { .. } | InventoryUpdateEvent::EntityCollectFailed { .. }
| InventoryUpdateEvent::BlockCollectFailed { .. } | InventoryUpdateEvent::BlockCollectFailed { .. }
| InventoryUpdateEvent::Craft => { | InventoryUpdateEvent::Craft => {
global_state global_state.audio.emit_ui_sfx(sfx_trigger_item, Some(1.0));
.audio
.emit_sfx_item(sfx_trigger_item, Some(1.0));
}, },
_ => global_state.audio.emit_sfx( _ => global_state.audio.emit_sfx(
sfx_trigger_item, sfx_trigger_item,
@ -1207,6 +1205,9 @@ impl PlayState for SessionState {
}, },
HudEvent::Logout => { HudEvent::Logout => {
self.client.borrow_mut().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_ambient_sounds();
global_state.audio.stop_all_sfx(); global_state.audio.stop_all_sfx();
return PlayStateResult::Pop; return PlayStateResult::Pop;