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",
length: 27.0,
length: 26.0,
tag: Leaves,
),
]

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

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.
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"}

View File

@ -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<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)
}
volume_multiplier.clamped(0.0, 1.0)
}
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {

View File

@ -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

View File

@ -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<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
/// 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<f32>,
vol: Option<f32>,
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<f32>,
) {
// Find sound based on given trigger_item
// Randomizes if multiple sounds are found
// Errors if no sounds are found
if let Some((event, item)) = trigger_item {
let sfx_file = match item.files.len() {
0 => {
debug!("Sfx event {:?} is missing audio file.", event);
"voxygen.audio.sfx.placeholder"
},
1 => item
.files
.last()
.expect("Failed to determine sound file for this trigger item."),
_ => {
// If more than one file is listed, choose one at random
let rand_step = rand::random::<usize>() % item.files.len();
&item.files[rand_step]
},
};
// 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<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
/// 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<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:
// 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) {

View File

@ -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;

View File

@ -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::<Uid>();
let uids = client.state().ecs().read_storage::<Uid>();
// 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));
}
}
},

View File

@ -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)

View File

@ -65,7 +65,7 @@ impl LodData {
lod_base: &[u32],
lod_alt: &[u32],
lod_horizon: &[u32],
clouds_size: Vec2<u32>,
weather_size: Vec2<u32>,
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 {

View File

@ -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::<Globals>() % 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::<Self>() % 16);
Self {
view_mat: view_mat.into_col_arrays(),
proj_mat: proj_mat.into_col_arrays(),

View File

@ -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::<Locals>() % 16 == 0);
impl Locals {
pub fn new(
shadow_mat: Mat4<f32>,
texture_mat: Mat4<f32>,
rain_occlusion_matrices: Mat4<f32>,
rain_occlusion_texture_mat: Mat4<f32>,
rel_rain_dir_mat: Mat4<f32>,
integrated_rain_vel: f32,
) -> Self {
// dbg!(core::mem::size_of::<Self>() % 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,

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_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);
},
}
}

View File

@ -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<RainOcclusionPassDrawer> {
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::<terrain::Vertex>(&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::<terrain::Vertex>(&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::<terrain::Vertex>(&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::<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::{
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,

View File

@ -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(),

View File

@ -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<f32> = 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<V: RectRasterableVol> Terrain<V> {
// 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::<f32> {
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_::<f64>(),
@ -1490,7 +1492,7 @@ impl<V: RectRasterableVol> Terrain<V> {
.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<f32>,

View File

@ -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;