implemented ability to select bit depth and refresh rate and implemented seperate setting for fullscreen resolution

This commit is contained in:
IBotDEU 2020-08-09 20:30:22 +00:00 committed by Imbris
parent 01ad81322d
commit 6b707a1ab7
10 changed files with 433 additions and 20 deletions

View File

@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add detection of entities under the cursor - Add detection of entities under the cursor
- Functional group-system with exp-sharing and disabled damage to group members - Functional group-system with exp-sharing and disabled damage to group members
- Some Campfire, fireball & bomb; particle, light & sound effects. - Some Campfire, fireball & bomb; particle, light & sound effects.
- Added setting to change resolution
### Changed ### Changed

1
Cargo.lock generated
View File

@ -4719,6 +4719,7 @@ dependencies = [
"guillotiere", "guillotiere",
"hashbrown", "hashbrown",
"image", "image",
"itertools",
"msgbox", "msgbox",
"num 0.2.1", "num 0.2.1",
"old_school_gfx_glutin_ext", "old_school_gfx_glutin_ext",

View File

@ -70,6 +70,7 @@ VoxygenLocalization(
"common.fatal_error": "Fataler Fehler", "common.fatal_error": "Fataler Fehler",
"common.decline": "Ablehnen", "common.decline": "Ablehnen",
"common.you": "Ihr", "common.you": "Ihr",
"common.automatic": "Auto",
/// End Common section /// End Common section
// Message when connection to the server is lost // Message when connection to the server is lost
@ -297,6 +298,10 @@ magischen Gegenstände ergattern?"#,
"hud.settings.fluid_rendering_mode.cheap": "Niedrig", "hud.settings.fluid_rendering_mode.cheap": "Niedrig",
"hud.settings.fluid_rendering_mode.shiny": "Hoch", "hud.settings.fluid_rendering_mode.shiny": "Hoch",
"hud.settings.cloud_rendering_mode.regular": "Realistisch", "hud.settings.cloud_rendering_mode.regular": "Realistisch",
"hud.settings.particles": "Partikel",
"hud.settings.resolution": "Auflösung",
"hud.settings.bit_depth": "Bittiefe",
"hud.settings.refresh_rate": "Bildwiederholrate",
"hud.settings.fullscreen": "Vollbild", "hud.settings.fullscreen": "Vollbild",
"hud.settings.save_window_size": "Größe speichern", "hud.settings.save_window_size": "Größe speichern",

View File

@ -72,6 +72,7 @@ VoxygenLocalization(
"common.error": "Error", "common.error": "Error",
"common.fatal_error": "Fatal Error", "common.fatal_error": "Fatal Error",
"common.you": "You", "common.you": "You",
"common.automatic": "Auto",
// Message when connection to the server is lost // Message when connection to the server is lost
"common.connection_lost": r#"Connection lost! "common.connection_lost": r#"Connection lost!
@ -298,6 +299,9 @@ magically infused items?"#,
"hud.settings.fluid_rendering_mode.shiny": "Shiny", "hud.settings.fluid_rendering_mode.shiny": "Shiny",
"hud.settings.cloud_rendering_mode.regular": "Regular", "hud.settings.cloud_rendering_mode.regular": "Regular",
"hud.settings.particles": "Particles", "hud.settings.particles": "Particles",
"hud.settings.resolution": "Resolution",
"hud.settings.bit_depth": "Bit Depth",
"hud.settings.refresh_rate": "Refresh Rate",
"hud.settings.fullscreen": "Fullscreen", "hud.settings.fullscreen": "Fullscreen",
"hud.settings.save_window_size": "Save window size", "hud.settings.save_window_size": "Save window size",

View File

@ -69,6 +69,7 @@ bincode = "1.2"
deunicode = "1.0" deunicode = "1.0"
uvth = "3.1.1" uvth = "3.1.1"
const-tweaker = { version = "0.3.1", optional = true } const-tweaker = { version = "0.3.1", optional = true }
itertools = "0.9.0"
# Logging # Logging
tracing = "0.1" tracing = "0.1"

View File

@ -283,6 +283,9 @@ pub enum Event {
ChangeAaMode(AaMode), ChangeAaMode(AaMode),
ChangeCloudMode(CloudMode), ChangeCloudMode(CloudMode),
ChangeFluidMode(FluidMode), ChangeFluidMode(FluidMode),
ChangeResolution([u16; 2]),
ChangeBitDepth(Option<u16>),
ChangeRefreshRate(Option<u16>),
CrosshairTransp(f32), CrosshairTransp(f32),
ChatTransp(f32), ChatTransp(f32),
ChatCharName(bool), ChatCharName(bool),
@ -1908,6 +1911,15 @@ impl Hud {
settings_window::Event::ChangeFluidMode(new_fluid_mode) => { settings_window::Event::ChangeFluidMode(new_fluid_mode) => {
events.push(Event::ChangeFluidMode(new_fluid_mode)); events.push(Event::ChangeFluidMode(new_fluid_mode));
}, },
settings_window::Event::ChangeResolution(new_resolution) => {
events.push(Event::ChangeResolution(new_resolution));
},
settings_window::Event::ChangeBitDepth(new_bit_depth) => {
events.push(Event::ChangeBitDepth(new_bit_depth));
},
settings_window::Event::ChangeRefreshRate(new_refresh_rate) => {
events.push(Event::ChangeRefreshRate(new_refresh_rate));
},
settings_window::Event::ChangeLanguage(language) => { settings_window::Event::ChangeLanguage(language) => {
events.push(Event::ChangeLanguage(language)); events.push(Event::ChangeLanguage(language));
}, },

View File

@ -17,6 +17,10 @@ use conrod_core::{
WidgetCommon, WidgetCommon,
}; };
use itertools::Itertools;
use std::iter::once;
use winit::monitor::VideoMode;
const FPS_CHOICES: [u32; 11] = [15, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500]; const FPS_CHOICES: [u32; 11] = [15, 30, 40, 50, 60, 90, 120, 144, 240, 300, 500];
widget_ids! { widget_ids! {
@ -111,8 +115,17 @@ widget_ids! {
cloud_mode_list, cloud_mode_list,
fluid_mode_text, fluid_mode_text,
fluid_mode_list, fluid_mode_list,
//
resolution,
resolution_label,
bit_depth,
bit_depth_label,
refresh_rate,
refresh_rate_label,
//
particles_button, particles_button,
particles_label, particles_label,
//
fullscreen_button, fullscreen_button,
fullscreen_label, fullscreen_label,
save_window_size_button, save_window_size_button,
@ -239,6 +252,9 @@ pub enum Event {
ChangeAaMode(AaMode), ChangeAaMode(AaMode),
ChangeCloudMode(CloudMode), ChangeCloudMode(CloudMode),
ChangeFluidMode(FluidMode), ChangeFluidMode(FluidMode),
ChangeResolution([u16; 2]),
ChangeBitDepth(Option<u16>),
ChangeRefreshRate(Option<u16>),
AdjustMusicVolume(f32), AdjustMusicVolume(f32),
AdjustSfxVolume(f32), AdjustSfxVolume(f32),
ChangeAudioDevice(String), ChangeAudioDevice(String),
@ -2039,11 +2055,168 @@ impl<'a> Widget for SettingsWindow<'a> {
events.push(Event::ToggleParticlesEnabled(particles_enabled)); events.push(Event::ToggleParticlesEnabled(particles_enabled));
} }
// Resolution, Bit Depth and Refresh Rate
let video_modes: Vec<VideoMode> = self
.global_state
.window
.window()
.window()
.current_monitor()
.video_modes()
.collect();
// Resolution
let resolutions: Vec<[u16; 2]> = video_modes
.iter()
.sorted_by_key(|mode| mode.size().height)
.sorted_by_key(|mode| mode.size().width)
.map(|mode| [mode.size().width as u16, mode.size().height as u16])
.dedup()
.collect();
Text::new(&self.localized_strings.get("hud.settings.resolution"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.particles_label, 8.0)
.color(TEXT_COLOR)
.set(state.ids.resolution_label, ui);
if let Some(clicked) = DropDownList::new(
resolutions
.iter()
.map(|res| format!("{}x{}", res[0], res[1]))
.collect::<Vec<String>>()
.as_slice(),
resolutions
.iter()
.position(|res| res == &self.global_state.settings.graphics.resolution),
)
.w_h(128.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.opensans.conrod_id)
.down_from(state.ids.resolution_label, 10.0)
.set(state.ids.resolution, ui)
{
events.push(Event::ChangeResolution(resolutions[clicked]));
}
// Bit Depth and Refresh Rate
let correct_res: Vec<VideoMode> = video_modes
.into_iter()
.filter(|mode| {
mode.size().width == self.global_state.settings.graphics.resolution[0] as u32
})
.filter(|mode| {
mode.size().height == self.global_state.settings.graphics.resolution[1] as u32
})
.collect();
// Bit Depth
let bit_depths: Vec<u16> = correct_res
.iter()
.filter(
|mode| match self.global_state.settings.graphics.refresh_rate {
Some(refresh_rate) => mode.refresh_rate() == refresh_rate,
None => true,
},
)
.sorted_by_key(|mode| mode.bit_depth())
.map(|mode| mode.bit_depth())
.rev()
.dedup()
.collect();
Text::new(&self.localized_strings.get("hud.settings.bit_depth"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.particles_label, 8.0)
.right_from(state.ids.resolution, 8.0)
.color(TEXT_COLOR)
.set(state.ids.bit_depth_label, ui);
if let Some(clicked) = DropDownList::new(
once(String::from(self.localized_strings.get("common.automatic")))
.chain(bit_depths.iter().map(|depth| format!("{}", depth)))
.collect::<Vec<String>>()
.as_slice(),
match self.global_state.settings.graphics.bit_depth {
Some(bit_depth) => bit_depths
.iter()
.position(|depth| depth == &bit_depth)
.map(|index| index + 1),
None => Some(0),
},
)
.w_h(128.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.opensans.conrod_id)
.down_from(state.ids.bit_depth_label, 10.0)
.right_from(state.ids.resolution, 8.0)
.set(state.ids.bit_depth, ui)
{
events.push(Event::ChangeBitDepth(if clicked == 0 {
None
} else {
Some(bit_depths[clicked - 1])
}));
}
// Refresh Rate
let refresh_rates: Vec<u16> = correct_res
.into_iter()
.filter(|mode| match self.global_state.settings.graphics.bit_depth {
Some(bit_depth) => mode.bit_depth() == bit_depth,
None => true,
})
.sorted_by_key(|mode| mode.refresh_rate())
.map(|mode| mode.refresh_rate())
.rev()
.dedup()
.collect();
Text::new(&self.localized_strings.get("hud.settings.refresh_rate"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.particles_label, 8.0)
.right_from(state.ids.bit_depth, 8.0)
.color(TEXT_COLOR)
.set(state.ids.refresh_rate_label, ui);
if let Some(clicked) = DropDownList::new(
once(String::from(self.localized_strings.get("common.automatic")))
.chain(refresh_rates.iter().map(|rate| format!("{}", rate)))
.collect::<Vec<String>>()
.as_slice(),
match self.global_state.settings.graphics.refresh_rate {
Some(refresh_rate) => refresh_rates
.iter()
.position(|rate| rate == &refresh_rate)
.map(|index| index + 1),
None => Some(0),
},
)
.w_h(128.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.opensans.conrod_id)
.down_from(state.ids.refresh_rate_label, 10.0)
.right_from(state.ids.bit_depth, 8.0)
.set(state.ids.refresh_rate, ui)
{
events.push(Event::ChangeRefreshRate(if clicked == 0 {
None
} else {
Some(refresh_rates[clicked - 1])
}));
}
// Fullscreen // Fullscreen
Text::new(&self.localized_strings.get("hud.settings.fullscreen")) Text::new(&self.localized_strings.get("hud.settings.fullscreen"))
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.particles_label, 8.0) .down_from(state.ids.resolution, 8.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.fullscreen_label, ui); .set(state.ids.fullscreen_label, ui);

View File

@ -949,6 +949,37 @@ impl PlayState for SessionState {
global_state.settings.graphics.fluid_mode = new_fluid_mode; global_state.settings.graphics.fluid_mode = new_fluid_mode;
global_state.settings.save_to_file_warn(); global_state.settings.save_to_file_warn();
}, },
HudEvent::ChangeResolution(new_resolution) => {
// Do this first so if it crashes the setting isn't saved :)
global_state.window.fullscreen(
global_state.settings.graphics.fullscreen,
new_resolution,
global_state.settings.graphics.bit_depth,
global_state.settings.graphics.refresh_rate,
);
global_state.settings.graphics.resolution = new_resolution;
global_state.settings.save_to_file_warn();
},
HudEvent::ChangeBitDepth(new_bit_depth) => {
global_state.window.fullscreen(
global_state.settings.graphics.fullscreen,
global_state.settings.graphics.resolution,
new_bit_depth,
global_state.settings.graphics.refresh_rate,
);
global_state.settings.graphics.bit_depth = new_bit_depth;
global_state.settings.save_to_file_warn();
},
HudEvent::ChangeRefreshRate(new_refresh_rate) => {
global_state.window.fullscreen(
global_state.settings.graphics.fullscreen,
global_state.settings.graphics.resolution,
global_state.settings.graphics.bit_depth,
new_refresh_rate,
);
global_state.settings.graphics.refresh_rate = new_refresh_rate;
global_state.settings.save_to_file_warn();
},
HudEvent::ChangeLanguage(new_language) => { HudEvent::ChangeLanguage(new_language) => {
global_state.settings.language.selected_language = global_state.settings.language.selected_language =
new_language.language_identifier; new_language.language_identifier;

View File

@ -619,6 +619,9 @@ pub struct GraphicsSettings {
pub aa_mode: AaMode, pub aa_mode: AaMode,
pub cloud_mode: CloudMode, pub cloud_mode: CloudMode,
pub fluid_mode: FluidMode, pub fluid_mode: FluidMode,
pub resolution: [u16; 2],
pub bit_depth: Option<u16>,
pub refresh_rate: Option<u16>,
pub window_size: [u16; 2], pub window_size: [u16; 2],
pub fullscreen: bool, pub fullscreen: bool,
} }
@ -636,6 +639,9 @@ impl Default for GraphicsSettings {
aa_mode: AaMode::Fxaa, aa_mode: AaMode::Fxaa,
cloud_mode: CloudMode::Regular, cloud_mode: CloudMode::Regular,
fluid_mode: FluidMode::Shiny, fluid_mode: FluidMode::Shiny,
resolution: [1920, 1080],
bit_depth: None,
refresh_rate: None,
window_size: [1920, 1080], window_size: [1920, 1080],
fullscreen: false, fullscreen: false,
} }

View File

@ -7,11 +7,13 @@ use crate::{
use crossbeam::channel; use crossbeam::channel;
use gilrs::{EventType, Gilrs}; use gilrs::{EventType, Gilrs};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools;
use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt}; use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use vek::*; use vek::*;
use winit::monitor::VideoMode;
/// Represents a key that the game recognises after input mapping. /// Represents a key that the game recognises after input mapping.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
@ -610,7 +612,12 @@ impl Window {
toggle_fullscreen: false, toggle_fullscreen: false,
}; };
this.fullscreen(settings.graphics.fullscreen); this.fullscreen(
settings.graphics.fullscreen,
settings.graphics.resolution,
settings.graphics.bit_depth,
settings.graphics.refresh_rate,
);
Ok((this, event_loop)) Ok((this, event_loop))
} }
@ -1056,34 +1063,206 @@ impl Window {
} }
pub fn toggle_fullscreen(&mut self, settings: &mut Settings) { pub fn toggle_fullscreen(&mut self, settings: &mut Settings) {
self.fullscreen(!self.is_fullscreen()); self.fullscreen(
!self.is_fullscreen(),
settings.graphics.resolution,
settings.graphics.bit_depth,
settings.graphics.refresh_rate,
);
settings.graphics.fullscreen = self.is_fullscreen(); settings.graphics.fullscreen = self.is_fullscreen();
settings.save_to_file_warn(); settings.save_to_file_warn();
} }
pub fn is_fullscreen(&self) -> bool { self.fullscreen } pub fn is_fullscreen(&self) -> bool { self.fullscreen }
pub fn fullscreen(&mut self, fullscreen: bool) { pub fn select_video_mode_rec(
&self,
resolution: [u16; 2],
bit_depth: Option<u16>,
refresh_rate: Option<u16>,
correct_res: Option<Vec<VideoMode>>,
correct_depth: Option<Option<VideoMode>>,
correct_rate: Option<Option<VideoMode>>,
) -> Option<VideoMode> {
// if a previous iteration of this method filtered the available video modes for
// the correct resolution already, load that value, otherwise filter it
// in this iteration
let correct_res = correct_res.unwrap_or_else(|| {
let window = self.window.window();
window
.current_monitor()
.video_modes()
.filter(|mode| mode.size().width == resolution[0] as u32)
.filter(|mode| mode.size().height == resolution[1] as u32)
.collect()
});
match bit_depth {
// A bit depth is given
Some(depth) => {
// analogous to correct_res
let correct_depth = correct_depth.unwrap_or_else(|| {
correct_res
.iter()
.find(|mode| mode.bit_depth() == depth)
.cloned()
});
match refresh_rate {
// A bit depth and a refresh rate is given
Some(rate) => {
// analogous to correct_res
let correct_rate = correct_rate.unwrap_or_else(|| {
correct_res
.iter()
.find(|mode| mode.refresh_rate() == rate)
.cloned()
});
// if no video mode with the given bit depth and refresh rate exists, fall
// back to a video mode that fits the resolution and either bit depth or
// refresh rate depending on which parameter was causing the correct video
// mode not to be found
correct_res
.iter()
.filter(|mode| mode.bit_depth() == depth)
.find(|mode| mode.refresh_rate() == rate)
.cloned()
.or_else(|| {
if correct_depth.is_none() && correct_rate.is_none() {
warn!(
"Bit depth and refresh rate specified in settings are \
incompatible with the monitor. Choosing highest bit \
depth and refresh rate possible instead."
);
}
self.select_video_mode_rec(
resolution,
correct_depth.is_some().then_some(depth),
correct_rate.is_some().then_some(rate),
Some(correct_res),
Some(correct_depth),
Some(correct_rate),
)
})
},
// A bit depth and no refresh rate is given
// if no video mode with the given bit depth exists, fall
// back to a video mode that fits only the resolution
None => match correct_depth {
Some(mode) => Some(mode),
None => {
warn!(
"Bit depth specified in settings is incompatible with the \
monitor. Choosing highest bit depth possible instead."
);
self.select_video_mode_rec(
resolution,
None,
None,
Some(correct_res),
Some(correct_depth),
None,
)
},
},
}
},
// No bit depth is given
None => match refresh_rate {
// No bit depth and a refresh rate is given
Some(rate) => {
// analogous to correct_res
let correct_rate = correct_rate.unwrap_or_else(|| {
correct_res
.iter()
.find(|mode| mode.refresh_rate() == rate)
.cloned()
});
// if no video mode with the given bit depth exists, fall
// back to a video mode that fits only the resolution
match correct_rate {
Some(mode) => Some(mode),
None => {
warn!(
"Refresh rate specified in settings is incompatible with the \
monitor. Choosing highest refresh rate possible instead."
);
self.select_video_mode_rec(
resolution,
None,
None,
Some(correct_res),
None,
Some(correct_rate),
)
},
}
},
// No bit depth and no refresh rate is given
// get the video mode with the specified resolution and the max bit depth and
// refresh rate
None => correct_res
.into_iter()
// Prefer bit depth over refresh rate
.sorted_by_key(|mode| mode.bit_depth())
.max_by_key(|mode| mode.refresh_rate()),
},
}
}
pub fn select_video_mode(
&self,
resolution: [u16; 2],
bit_depth: Option<u16>,
refresh_rate: Option<u16>,
) -> VideoMode {
// (resolution, bit depth, refresh rate) represents a video mode
// spec: as specified
// max: maximum value available
// order of fallbacks as follows:
// (spec, spec, spec)
// (spec, spec, max), (spec, max, spec)
// (spec, max, max)
// (max, max, max)
self.select_video_mode_rec(resolution, bit_depth, refresh_rate, None, None, None)
// if there is no video mode with the specified resolution, fall back to the video mode with max resolution, bit depth and refresh rate
.unwrap_or_else(|| {
warn!(
"Resolution specified in settings is incompatible with the monitor. Choosing \
highest resolution possible instead."
);
self
.window
.window()
.current_monitor()
.video_modes()
// Prefer bit depth over refresh rate
.sorted_by_key(|mode| mode.refresh_rate())
.sorted_by_key(|mode| mode.bit_depth())
.max_by_key(|mode| mode.size().width)
.expect("No video modes available!!")
})
}
pub fn fullscreen(
&mut self,
fullscreen: bool,
resolution: [u16; 2],
bit_depth: Option<u16>,
refresh_rate: Option<u16>,
) {
let window = self.window.window(); let window = self.window.window();
self.fullscreen = fullscreen; self.fullscreen = fullscreen;
if fullscreen { if fullscreen {
window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive(
window self.select_video_mode(resolution, bit_depth, refresh_rate),
.current_monitor()
.video_modes()
.filter(|mode| mode.bit_depth() >= 24 && mode.refresh_rate() >= 59)
.max_by_key(|mode| mode.size().width)
.unwrap_or_else(|| {
warn!(
"No video mode with a bit depth of at least 24 and a refresh rate of \
at least 60Hz found"
);
window
.current_monitor()
.video_modes()
.max_by_key(|mode| mode.size().width)
.expect("No video modes available!!")
}),
))); )));
} else { } else {
window.set_fullscreen(None); window.set_fullscreen(None);