From 432e8285175a7eef896b0b6b4f3b166c775e82a2 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 25 Sep 2019 23:19:45 -0400 Subject: [PATCH 1/4] Add MSAAx4 --- assets/voxygen/shaders/postprocess-frag.glsl | 11 +- voxygen/src/render/error.rs | 94 +++++++++++++++ voxygen/src/render/mod.rs | 15 +-- voxygen/src/render/renderer.rs | 120 ++++++++++--------- 4 files changed, 167 insertions(+), 73 deletions(-) create mode 100644 voxygen/src/render/error.rs diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index 1a5ba3113a..fe1aa3cf81 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -2,7 +2,7 @@ #include -uniform sampler2D src_color; +uniform sampler2DMS src_color; in vec2 f_pos; @@ -169,8 +169,15 @@ void main() { uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); } - vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy * FXAA_SCALE, screen_res.xy * FXAA_SCALE); + + //vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy * FXAA_SCALE, screen_res.xy * FXAA_SCALE); //vec4 fxaa_color = texture(src_color, uv); + ivec2 uv_int = ivec2(uv.x * screen_res.x, uv.y * screen_res.y); + vec4 sample1 = texelFetch(src_color, uv_int, 0); + vec4 sample2 = texelFetch(src_color, uv_int, 1); + vec4 sample3 = texelFetch(src_color, uv_int, 2); + vec4 sample4 = texelFetch(src_color, uv_int, 3); + vec4 fxaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0; vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); hsva_color.y *= 1.45; diff --git a/voxygen/src/render/error.rs b/voxygen/src/render/error.rs new file mode 100644 index 0000000000..a7a38d784d --- /dev/null +++ b/voxygen/src/render/error.rs @@ -0,0 +1,94 @@ +/// Used to represent one of many possible errors that may be omitted by the rendering subsystem. +#[derive(Debug)] +pub enum RenderError { + PipelineError(gfx::PipelineStateError), + UpdateError(gfx::UpdateError), + TexUpdateError(gfx::UpdateError<[u16; 3]>), + CombinedError(gfx::CombinedError), + BufferCreationError(gfx::buffer::CreationError), + IncludeError(glsl_include::Error), + MappingError(gfx::mapping::Error), + CopyError(gfx::CopyError<[u16; 3], usize>), +} + +impl From> for RenderError { + fn from(err: gfx::PipelineStateError) -> Self { + Self::PipelineError(err) + } +} + +impl From> for RenderError { + fn from(err: gfx::PipelineStateError<&str>) -> Self { + match err { + gfx::PipelineStateError::DescriptorInit(err) => { + gfx::PipelineStateError::DescriptorInit(err.into()) + } + err => err, + } + .into() + } +} +impl From for RenderError { + fn from(err: gfx::shade::ProgramError) -> Self { + gfx::PipelineStateError::::Program(err).into() + } +} +impl From> for RenderError { + fn from(err: gfx::UpdateError) -> Self { + Self::UpdateError(err) + } +} + +impl From> for RenderError { + fn from(err: gfx::UpdateError<[u16; 3]>) -> Self { + Self::TexUpdateError(err) + } +} + +impl From for RenderError { + fn from(err: gfx::CombinedError) -> Self { + Self::CombinedError(err) + } +} + +impl From for RenderError { + fn from(err: gfx::TargetViewError) -> Self { + Self::CombinedError(err.into()) + } +} + +impl From for RenderError { + fn from(err: gfx::ResourceViewError) -> Self { + Self::CombinedError(err.into()) + } +} + +impl From for RenderError { + fn from(err: gfx::texture::CreationError) -> Self { + Self::CombinedError(err.into()) + } +} + +impl From for RenderError { + fn from(err: gfx::buffer::CreationError) -> Self { + Self::BufferCreationError(err) + } +} + +impl From for RenderError { + fn from(err: glsl_include::Error) -> Self { + Self::IncludeError(err) + } +} + +impl From for RenderError { + fn from(err: gfx::mapping::Error) -> Self { + Self::MappingError(err) + } +} + +impl From> for RenderError { + fn from(err: gfx::CopyError<[u16; 3], usize>) -> Self { + Self::CopyError(err) + } +} diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index b7cd14b426..182e604b6d 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -1,4 +1,5 @@ pub mod consts; +mod error; pub mod instances; pub mod mesh; pub mod model; @@ -10,6 +11,7 @@ mod util; // Reexports pub use self::{ consts::Consts, + error::RenderError, instances::Instances, mesh::{Mesh, Quad, Tri}, model::{DynamicModel, Model}, @@ -37,19 +39,6 @@ use gfx_device_gl as gfx_backend; use gfx; -/// Used to represent one of many possible errors that may be omitted by the rendering subsystem. -#[derive(Debug)] -pub enum RenderError { - PipelineError(gfx::PipelineStateError), - UpdateError(gfx::UpdateError), - TexUpdateError(gfx::UpdateError<[u16; 3]>), - CombinedError(gfx::CombinedError), - BufferCreationError(gfx::buffer::CreationError), - IncludeError(glsl_include::Error), - MappingError(gfx::mapping::Error), - CopyError(gfx::CopyError<[u16; 3], usize>), -} - /// Used to represent a specific rendering configuration. /// /// Note that pipelines are tied to the diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 25eb0a4bc5..33e48acafd 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -169,12 +169,35 @@ impl Renderer { factory: &mut gfx_device_gl::Factory, size: (u16, u16), ) -> Result<(TgtColorView, TgtDepthView, TgtColorRes), RenderError> { - let (_, tgt_color_res, tgt_color_view) = factory - .create_render_target::(size.0, size.1) - .map_err(RenderError::CombinedError)?;; - let tgt_depth_view = factory - .create_depth_stencil_view_only::(size.0, size.1) - .map_err(RenderError::CombinedError)?;; + let kind = gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Multi(4)); + let levels = 1; + + let color_cty = <::Channel as gfx::format::ChannelTyped + >::get_channel_type(); + let tgt_color_tex = factory.create_texture( + kind, + levels, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::RENDER_TARGET, + gfx::memory::Usage::Data, + Some(color_cty), + )?; + let tgt_color_res = factory.view_texture_as_shader_resource::( + &tgt_color_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + let tgt_color_view = factory.view_texture_as_render_target(&tgt_color_tex, 0, None)?; + + let depth_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); + let tgt_depth_tex = factory.create_texture( + kind, + levels, + gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_cty), + )?; + let tgt_depth_view = factory.view_texture_as_depth_stencil_trivial(&tgt_depth_tex)?; + Ok((tgt_color_view, tgt_depth_view, tgt_color_res)) } @@ -319,33 +342,29 @@ impl Renderer { type WinSurfaceData = <::Surface as SurfaceTyped>::DataType; let download = self .factory - .create_download_buffer::(width as usize * height as usize) - .map_err(|err| RenderError::BufferCreationError(err))?; - self.encoder - .copy_texture_to_buffer_raw( - self.win_color_view.raw().get_texture(), - None, - gfx::texture::RawImageInfo { - xoffset: 0, - yoffset: 0, - zoffset: 0, - width, - height, - depth: 0, - format: WinColorFmt::get_format(), - mipmap: 0, - }, - download.raw(), - 0, - ) - .map_err(|err| RenderError::CopyError(err))?; + .create_download_buffer::(width as usize * height as usize)?; + self.encoder.copy_texture_to_buffer_raw( + self.win_color_view.raw().get_texture(), + None, + gfx::texture::RawImageInfo { + xoffset: 0, + yoffset: 0, + zoffset: 0, + width, + height, + depth: 0, + format: WinColorFmt::get_format(), + mipmap: 0, + }, + download.raw(), + 0, + )?; self.flush(); // Assumes that the format is Rgba8. let raw_data = self .factory - .read_mapping(&download) - .map_err(|err| RenderError::MappingError(err))? + .read_mapping(&download)? .chunks_exact(width as usize) .rev() .flatten() @@ -722,38 +741,23 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( ctx: &IncludeContext, cull_face: gfx::state::CullFace, ) -> Result, RenderError> { - let vs = ctx.expand(vs).map_err(RenderError::IncludeError)?; - let fs = ctx.expand(fs).map_err(RenderError::IncludeError)?; + let vs = ctx.expand(vs)?; + let fs = ctx.expand(fs)?; - let program = factory - .link_program(vs.as_bytes(), fs.as_bytes()) - .map_err(|err| RenderError::PipelineError(gfx::PipelineStateError::Program(err)))?; + let program = factory.link_program(vs.as_bytes(), fs.as_bytes())?; Ok(GfxPipeline { - pso: factory - .create_pipeline_from_program( - &program, - gfx::Primitive::TriangleList, - gfx::state::Rasterizer { - front_face: gfx::state::FrontFace::CounterClockwise, - cull_face, - method: gfx::state::RasterMethod::Fill, - offset: None, - samples: Some(gfx::state::MultiSample), - }, - pipe, - ) - // Do some funky things to work around an oddity in gfx's error ownership rules. - .map_err(|err| { - RenderError::PipelineError(match err { - gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err), - gfx::PipelineStateError::DescriptorInit(err) => { - gfx::PipelineStateError::DescriptorInit(err.into()) - } - gfx::PipelineStateError::DeviceCreate(err) => { - gfx::PipelineStateError::DeviceCreate(err) - } - }) - })?, + pso: factory.create_pipeline_from_program( + &program, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + front_face: gfx::state::FrontFace::CounterClockwise, + cull_face, + method: gfx::state::RasterMethod::Fill, + offset: None, + samples: Some(gfx::state::MultiSample), + }, + pipe, + )?, // Do some funky things to work around an oddity in gfx's error ownership rules. }) } From a3daa6065abce28a3d6dd48146f1ed83dcc1ed2a Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 26 Sep 2019 03:28:40 -0400 Subject: [PATCH 2/4] Add aa modes, with ui interface to switch --- assets/voxygen/shaders/antialias/fxaa.glsl | 137 ++++++++++++ .../voxygen/shaders/antialias/msaa-x16.glsl | 30 +++ assets/voxygen/shaders/antialias/msaa-x4.glsl | 15 ++ assets/voxygen/shaders/antialias/msaa-x8.glsl | 19 ++ assets/voxygen/shaders/antialias/none.glsl | 5 + assets/voxygen/shaders/postprocess-frag.glsl | 157 +------------- voxygen/src/hud/mod.rs | 6 +- voxygen/src/hud/settings_window.rs | 37 +++- voxygen/src/render/mod.rs | 12 ++ voxygen/src/render/renderer.rs | 117 +++++++--- voxygen/src/session.rs | 12 +- voxygen/src/settings.rs | 3 + voxygen/src/ui/mod.rs | 1 + voxygen/src/ui/widgets/mod.rs | 1 + voxygen/src/ui/widgets/radio_list.rs | 202 ++++++++++++++++++ voxygen/src/window.rs | 8 +- 16 files changed, 583 insertions(+), 179 deletions(-) create mode 100644 assets/voxygen/shaders/antialias/fxaa.glsl create mode 100644 assets/voxygen/shaders/antialias/msaa-x16.glsl create mode 100644 assets/voxygen/shaders/antialias/msaa-x4.glsl create mode 100644 assets/voxygen/shaders/antialias/msaa-x8.glsl create mode 100644 assets/voxygen/shaders/antialias/none.glsl create mode 100644 voxygen/src/ui/widgets/radio_list.rs diff --git a/assets/voxygen/shaders/antialias/fxaa.glsl b/assets/voxygen/shaders/antialias/fxaa.glsl new file mode 100644 index 0000000000..700dfa4e90 --- /dev/null +++ b/assets/voxygen/shaders/antialias/fxaa.glsl @@ -0,0 +1,137 @@ +uniform sampler2D src_color; + +const float FXAA_SCALE = 1.5; + +/** +Basic FXAA implementation based on the code on geeks3d.com with the +modification that the texture2DLod stuff was removed since it's +unsupported by WebGL. + +-- + +From: +https://github.com/mitsuhiko/webgl-meincraft + +Copyright (c) 2011 by Armin Ronacher. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FXAA_REDUCE_MIN + #define FXAA_REDUCE_MIN (1.0/ 128.0) +#endif +#ifndef FXAA_REDUCE_MUL + #define FXAA_REDUCE_MUL (1.0 / 8.0) +#endif +#ifndef FXAA_SPAN_MAX + #define FXAA_SPAN_MAX 8.0 +#endif + +//optimized version for mobile, where dependent +//texture reads can be a bottleneck +vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution, + vec2 v_rgbNW, vec2 v_rgbNE, + vec2 v_rgbSW, vec2 v_rgbSE, + vec2 v_rgbM) { + vec4 color; + mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); + vec3 rgbNW = texture(tex, v_rgbNW).xyz; + vec3 rgbNE = texture(tex, v_rgbNE).xyz; + vec3 rgbSW = texture(tex, v_rgbSW).xyz; + vec3 rgbSE = texture(tex, v_rgbSE).xyz; + vec4 texColor = texture(tex, v_rgbM); + vec3 rgbM = texColor.xyz; + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + mediump vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * + (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) * inverseVP; + + vec3 rgbA = 0.5 * ( + texture(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + + texture(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * ( + texture(tex, fragCoord * inverseVP + dir * -0.5).xyz + + texture(tex, fragCoord * inverseVP + dir * 0.5).xyz); + + float lumaB = dot(rgbB, luma); + if ((lumaB < lumaMin) || (lumaB > lumaMax)) + color = vec4(rgbA, texColor.a); + else + color = vec4(rgbB, texColor.a); + return color; +} + + +void texcoords(vec2 fragCoord, vec2 resolution, + out vec2 v_rgbNW, out vec2 v_rgbNE, + out vec2 v_rgbSW, out vec2 v_rgbSE, + out vec2 v_rgbM) { + vec2 inverseVP = 1.0 / resolution.xy; + v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; + v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; + v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; + v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; + v_rgbM = vec2(fragCoord * inverseVP); +} + + +vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { + mediump vec2 v_rgbNW; + mediump vec2 v_rgbNE; + mediump vec2 v_rgbSW; + mediump vec2 v_rgbSE; + mediump vec2 v_rgbM; + + vec2 scaled_fc = fragCoord * FXAA_SCALE; + vec2 scaled_res = resolution * FXAA_SCALE; + + //compute the texture coords + texcoords(scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); + + //compute FXAA + return fxaa(tex, scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/msaa-x16.glsl b/assets/voxygen/shaders/antialias/msaa-x16.glsl new file mode 100644 index 0000000000..c9fb6514b7 --- /dev/null +++ b/assets/voxygen/shaders/antialias/msaa-x16.glsl @@ -0,0 +1,30 @@ +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { + ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); + + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); + vec4 sample5 = texelFetch(tex, texel_coord, 4); + vec4 sample6 = texelFetch(tex, texel_coord, 5); + vec4 sample7 = texelFetch(tex, texel_coord, 6); + vec4 sample8 = texelFetch(tex, texel_coord, 7); + vec4 sample9 = texelFetch(tex, texel_coord, 8); + vec4 sample10 = texelFetch(tex, texel_coord, 9); + vec4 sample11 = texelFetch(tex, texel_coord, 11); + vec4 sample12 = texelFetch(tex, texel_coord, 12); + vec4 sample13 = texelFetch(tex, texel_coord, 13); + vec4 sample14 = texelFetch(tex, texel_coord, 14); + vec4 sample15 = texelFetch(tex, texel_coord, 15); + vec4 sample16 = texelFetch(tex, texel_coord, 16); + + // Average Samples + vec4 msaa_color = ( + sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7 + sample8 + + sample9 + sample10 + sample11 + sample12 + sample13 + sample14 + sample15 + sample16 + ) / 16.0; + + return msaa_color; +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/msaa-x4.glsl b/assets/voxygen/shaders/antialias/msaa-x4.glsl new file mode 100644 index 0000000000..287c701acd --- /dev/null +++ b/assets/voxygen/shaders/antialias/msaa-x4.glsl @@ -0,0 +1,15 @@ +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { + ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); + + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); + + // Average Samples + vec4 msaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0; + + return msaa_color; +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/msaa-x8.glsl b/assets/voxygen/shaders/antialias/msaa-x8.glsl new file mode 100644 index 0000000000..9a8652cf81 --- /dev/null +++ b/assets/voxygen/shaders/antialias/msaa-x8.glsl @@ -0,0 +1,19 @@ +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { + ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); + + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); + vec4 sample5 = texelFetch(tex, texel_coord, 4); + vec4 sample6 = texelFetch(tex, texel_coord, 5); + vec4 sample7 = texelFetch(tex, texel_coord, 6); + vec4 sample8 = texelFetch(tex, texel_coord, 7); + + // Average Samples + vec4 msaa_color = (sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7 + sample8) / 8.0; + + return msaa_color; +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/none.glsl b/assets/voxygen/shaders/antialias/none.glsl new file mode 100644 index 0000000000..d6230fa618 --- /dev/null +++ b/assets/voxygen/shaders/antialias/none.glsl @@ -0,0 +1,5 @@ +uniform sampler2D src_color; + +vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { + return texture(src_color, fragCoord / resolution); +} \ No newline at end of file diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index fe1aa3cf81..4193bda525 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -1,8 +1,8 @@ #version 330 core #include - -uniform sampler2DMS src_color; +// Note: The sampler uniform is declared here because it differs for MSAA +#include in vec2 f_pos; @@ -13,137 +13,6 @@ uniform u_locals { out vec4 tgt_color; -/** -Basic FXAA implementation based on the code on geeks3d.com with the -modification that the texture2DLod stuff was removed since it's -unsupported by WebGL. - --- - -From: -https://github.com/mitsuhiko/webgl-meincraft - -Copyright (c) 2011 by Armin Ronacher. - -Some rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef FXAA_REDUCE_MIN - #define FXAA_REDUCE_MIN (1.0/ 128.0) -#endif -#ifndef FXAA_REDUCE_MUL - #define FXAA_REDUCE_MUL (1.0 / 8.0) -#endif -#ifndef FXAA_SPAN_MAX - #define FXAA_SPAN_MAX 8.0 -#endif - -//optimized version for mobile, where dependent -//texture reads can be a bottleneck -vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution, - vec2 v_rgbNW, vec2 v_rgbNE, - vec2 v_rgbSW, vec2 v_rgbSE, - vec2 v_rgbM) { - vec4 color; - mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); - vec3 rgbNW = texture(tex, v_rgbNW).xyz; - vec3 rgbNE = texture(tex, v_rgbNE).xyz; - vec3 rgbSW = texture(tex, v_rgbSW).xyz; - vec3 rgbSE = texture(tex, v_rgbSE).xyz; - vec4 texColor = texture(tex, v_rgbM); - vec3 rgbM = texColor.xyz; - vec3 luma = vec3(0.299, 0.587, 0.114); - float lumaNW = dot(rgbNW, luma); - float lumaNE = dot(rgbNE, luma); - float lumaSW = dot(rgbSW, luma); - float lumaSE = dot(rgbSE, luma); - float lumaM = dot(rgbM, luma); - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - - mediump vec2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * - (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), - max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), - dir * rcpDirMin)) * inverseVP; - - vec3 rgbA = 0.5 * ( - texture(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + - texture(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * ( - texture(tex, fragCoord * inverseVP + dir * -0.5).xyz + - texture(tex, fragCoord * inverseVP + dir * 0.5).xyz); - - float lumaB = dot(rgbB, luma); - if ((lumaB < lumaMin) || (lumaB > lumaMax)) - color = vec4(rgbA, texColor.a); - else - color = vec4(rgbB, texColor.a); - return color; -} - - -void texcoords(vec2 fragCoord, vec2 resolution, - out vec2 v_rgbNW, out vec2 v_rgbNE, - out vec2 v_rgbSW, out vec2 v_rgbSE, - out vec2 v_rgbM) { - vec2 inverseVP = 1.0 / resolution.xy; - v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; - v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; - v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; - v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; - v_rgbM = vec2(fragCoord * inverseVP); -} - - -vec4 fxaa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { - mediump vec2 v_rgbNW; - mediump vec2 v_rgbNE; - mediump vec2 v_rgbSW; - mediump vec2 v_rgbSE; - mediump vec2 v_rgbM; - - //compute the texture coords - texcoords(fragCoord, resolution, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); - - //compute FXAA - return fxaa(tex, fragCoord, resolution, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); -} - vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); @@ -160,8 +29,6 @@ vec3 hsv2rgb(vec3 c) { return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -const float FXAA_SCALE = 1.5; - void main() { vec2 uv = (f_pos + 1.0) * 0.5; @@ -170,21 +37,15 @@ void main() { } - //vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy * FXAA_SCALE, screen_res.xy * FXAA_SCALE); - //vec4 fxaa_color = texture(src_color, uv); - ivec2 uv_int = ivec2(uv.x * screen_res.x, uv.y * screen_res.y); - vec4 sample1 = texelFetch(src_color, uv_int, 0); - vec4 sample2 = texelFetch(src_color, uv_int, 1); - vec4 sample3 = texelFetch(src_color, uv_int, 2); - vec4 sample4 = texelFetch(src_color, uv_int, 3); - vec4 fxaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0; + vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy); - vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); - hsva_color.y *= 1.45; - hsva_color.z *= 0.85; + //vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); + //hsva_color.y *= 1.45; + //hsva_color.z *= 0.85; //hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0); - vec4 final_color = fxaa_color; - //vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); + //vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); + + vec4 final_color = aa_color; if (medium.x == 1u) { final_color *= vec4(0.2, 0.2, 0.8, 1.0); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 83edafa662..d18c58ab13 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -32,7 +32,7 @@ use social::{Social, SocialTab}; use spell::Spell; use crate::{ - render::{Consts, Globals, Renderer}, + render::{AaMode, Consts, Globals, Renderer}, scene::camera::Camera, settings::ControlSettings, ui::{Ingameable, ScaleMode, Ui}, @@ -158,6 +158,7 @@ pub enum Event { ChangeAudioDevice(String), ChangeMaxFPS(u32), ChangeFOV(u16), + ChangeAaMode(AaMode), CrosshairTransp(f32), CrosshairType(CrosshairType), ToggleXpBar(XpBar), @@ -832,6 +833,9 @@ impl Hud { settings_window::Event::AdjustFOV(new_fov) => { events.push(Event::ChangeFOV(new_fov)); } + settings_window::Event::ChangeAaMode(new_aa_mode) => { + events.push(Event::ChangeAaMode(new_aa_mode)); + } } } } diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 8a0d43daa9..bc6a4da576 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -2,7 +2,8 @@ use super::{ img_ids::Imgs, BarNumbers, CrosshairType, Fonts, ShortcutNumbers, Show, XpBar, TEXT_COLOR, }; use crate::{ - ui::{ImageSlider, ScaleMode, ToggleButton}, + render::AaMode, + ui::{ImageSlider, RadioList, ScaleMode, ToggleButton}, GlobalState, }; use conrod_core::{ @@ -76,6 +77,8 @@ widget_ids! { fov_slider, fov_text, fov_value, + aa_radio_buttons, + aa_mode_text, audio_volume_slider, audio_volume_text, sfx_volume_slider, @@ -153,6 +156,7 @@ pub enum Event { AdjustMouseZoom(u32), AdjustViewDistance(u32), AdjustFOV(u16), + ChangeAaMode(AaMode), AdjustMusicVolume(f32), AdjustSfxVolume(f32), ChangeAudioDevice(String), @@ -1196,6 +1200,37 @@ impl<'a> Widget for SettingsWindow<'a> { .font_id(self.fonts.opensans) .color(TEXT_COLOR) .set(state.ids.fov_value, ui); + + // AaMode + Text::new("AntiAliasing Mode") + .down_from(state.ids.fov_slider, 8.0) + .font_size(14) + .font_id(self.fonts.opensans) + .color(TEXT_COLOR) + .set(state.ids.aa_mode_text, ui); + let mode_label_list = [ + (&AaMode::None, "No AA"), + (&AaMode::Fxaa, "FXAA"), + (&AaMode::MsaaX4, "MSAA x4"), + (&AaMode::MsaaX8, "MSAA x8"), + (&AaMode::MsaaX16, "MSAA x16 (experimental)"), + (&AaMode::SsaaX4, "SSAA x4"), + ]; + if let Some((_, mode)) = RadioList::new( + (0..mode_label_list.len()) + .find(|i| *mode_label_list[*i].0 == self.global_state.settings.graphics.aa_mode) + .unwrap_or(0), + self.imgs.check, + self.imgs.check_checked, + &mode_label_list, + ) + .down_from(state.ids.aa_mode_text, 8.0) + .text_color(TEXT_COLOR) + .font_size(12) + .set(state.ids.aa_radio_buttons, ui) + { + events.push(Event::ChangeAaMode(*mode)) + } } // 5) Sound Tab ----------------------------------- diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 182e604b6d..2ddee8cb1f 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -52,3 +52,15 @@ use gfx; pub trait Pipeline { type Vertex: Clone + gfx::traits::Pod + gfx::pso::buffer::Structure; } + +use serde_derive::{Deserialize, Serialize}; +/// Anti-aliasing modes +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum AaMode { + None, + Fxaa, + MsaaX4, + MsaaX8, + MsaaX16, + SsaaX4, +} diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 33e48acafd..b64888faf0 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -6,7 +6,7 @@ use super::{ model::{DynamicModel, Model}, pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light, Shadow}, texture::Texture, - Pipeline, RenderError, + AaMode, Pipeline, RenderError, }; use common::assets::{self, watch::ReloadIndicator}; use gfx::{ @@ -71,6 +71,8 @@ pub struct Renderer { postprocess_pipeline: GfxPipeline>, shader_reload_indicator: ReloadIndicator, + + aa_mode: AaMode, } impl Renderer { @@ -80,6 +82,7 @@ impl Renderer { mut factory: gfx_backend::Factory, win_color_view: WinColorView, win_depth_view: WinDepthView, + aa_mode: AaMode, ) -> Result { let mut shader_reload_indicator = ReloadIndicator::new(); @@ -91,11 +94,11 @@ impl Renderer { sprite_pipeline, ui_pipeline, postprocess_pipeline, - ) = create_pipelines(&mut factory, &mut shader_reload_indicator)?; + ) = create_pipelines(&mut factory, aa_mode, &mut shader_reload_indicator)?; let dims = win_color_view.get_dimensions(); let (tgt_color_view, tgt_depth_view, tgt_color_res) = - Self::create_rt_views(&mut factory, (dims.0, dims.1))?; + Self::create_rt_views(&mut factory, (dims.0, dims.1), aa_mode)?; let sampler = factory.create_sampler_linear(); @@ -122,6 +125,8 @@ impl Renderer { postprocess_pipeline, shader_reload_indicator, + + aa_mode, }) } @@ -149,6 +154,19 @@ impl Renderer { (&mut self.win_color_view, &mut self.win_depth_view) } + /// Change the anti-aliasing mode + pub fn set_aa_mode(&mut self, aa_mode: AaMode) -> Result<(), RenderError> { + self.aa_mode = aa_mode; + + // Recreate render target + self.on_resize()?; + + // Recreate pipelines with the new AA mode + self.recreate_pipelines(); + + Ok(()) + } + /// Resize internal render targets to match window render target dimensions. pub fn on_resize(&mut self) -> Result<(), RenderError> { let dims = self.win_color_view.get_dimensions(); @@ -156,7 +174,7 @@ impl Renderer { // Avoid panics when creating texture with w,h of 0,0. if dims.0 != 0 && dims.1 != 0 { let (tgt_color_view, tgt_depth_view, tgt_color_res) = - Self::create_rt_views(&mut self.factory, (dims.0, dims.1))?; + Self::create_rt_views(&mut self.factory, (dims.0, dims.1), self.aa_mode)?; self.tgt_color_res = tgt_color_res; self.tgt_color_view = tgt_color_view; self.tgt_depth_view = tgt_depth_view; @@ -168,8 +186,26 @@ impl Renderer { fn create_rt_views( factory: &mut gfx_device_gl::Factory, size: (u16, u16), + aa_mode: AaMode, ) -> Result<(TgtColorView, TgtDepthView, TgtColorRes), RenderError> { - let kind = gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Multi(4)); + let kind = match aa_mode { + AaMode::None | AaMode::Fxaa => { + gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Single) + } + // TODO: Ensure sampling in the shader is exactly between the 4 texels + AaMode::SsaaX4 => { + gfx::texture::Kind::D2(size.0 * 2, size.1 * 2, gfx::texture::AaMode::Single) + } + AaMode::MsaaX4 => { + gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Multi(4)) + } + AaMode::MsaaX8 => { + gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Multi(8)) + } + AaMode::MsaaX16 => { + gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Multi(16)) + } + }; let levels = 1; let color_cty = <::Channel as gfx::format::ChannelTyped @@ -222,29 +258,38 @@ impl Renderer { // If the shaders files were changed attempt to recreate the shaders if self.shader_reload_indicator.reloaded() { - match create_pipelines(&mut self.factory, &mut self.shader_reload_indicator) { - Ok(( - skybox_pipeline, - figure_pipeline, - terrain_pipeline, - fluid_pipeline, - sprite_pipeline, - ui_pipeline, - postprocess_pipeline, - )) => { - self.skybox_pipeline = skybox_pipeline; - self.figure_pipeline = figure_pipeline; - self.terrain_pipeline = terrain_pipeline; - self.fluid_pipeline = fluid_pipeline; - self.sprite_pipeline = sprite_pipeline; - self.ui_pipeline = ui_pipeline; - self.postprocess_pipeline = postprocess_pipeline; - } - Err(e) => error!( - "Could not recreate shaders from assets due to an error: {:#?}", - e - ), + self.recreate_pipelines(); + } + } + + /// Recreate the pipelines + fn recreate_pipelines(&mut self) { + match create_pipelines( + &mut self.factory, + self.aa_mode, + &mut self.shader_reload_indicator, + ) { + Ok(( + skybox_pipeline, + figure_pipeline, + terrain_pipeline, + fluid_pipeline, + sprite_pipeline, + ui_pipeline, + postprocess_pipeline, + )) => { + self.skybox_pipeline = skybox_pipeline; + self.figure_pipeline = figure_pipeline; + self.terrain_pipeline = terrain_pipeline; + self.fluid_pipeline = fluid_pipeline; + self.sprite_pipeline = sprite_pipeline; + self.ui_pipeline = ui_pipeline; + self.postprocess_pipeline = postprocess_pipeline; } + Err(e) => error!( + "Could not recreate shaders from assets due to an error: {:#?}", + e + ), } } @@ -595,6 +640,7 @@ struct GfxPipeline { /// Creates all the pipelines used to render. fn create_pipelines( factory: &mut gfx_backend::Factory, + aa_mode: AaMode, shader_reload_indicator: &mut ReloadIndicator, ) -> Result< ( @@ -624,12 +670,29 @@ fn create_pipelines( assets::load_watched::("voxygen.shaders.include.random", shader_reload_indicator) .unwrap(); + let anti_alias = assets::load_watched::( + &[ + "voxygen.shaders.antialias.", + match aa_mode { + AaMode::None | AaMode::SsaaX4 => "none", + AaMode::Fxaa => "fxaa", + AaMode::MsaaX4 => "msaa-x4", + AaMode::MsaaX8 => "msaa-x8", + AaMode::MsaaX16 => "msaa-x16", + }, + ] + .concat(), + shader_reload_indicator, + ) + .unwrap(); + let mut include_ctx = IncludeContext::new(); include_ctx.include("globals.glsl", &globals); include_ctx.include("sky.glsl", &sky); include_ctx.include("light.glsl", &light); include_ctx.include("srgb.glsl", &srgb); include_ctx.include("random.glsl", &random); + include_ctx.include("anti-aliasing.glsl", &anti_alias); // Construct a pipeline for rendering skyboxes let skybox_pipeline = create_pipeline( diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 0ad2b8dc54..fbdd326786 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -455,7 +455,17 @@ impl PlayState for SessionState { HudEvent::ChangeFOV(new_fov) => { global_state.settings.graphics.fov = new_fov; global_state.settings.save_to_file_warn(); - &self.scene.camera_mut().set_fov_deg(new_fov); + self.scene.camera_mut().set_fov_deg(new_fov); + } + HudEvent::ChangeAaMode(new_aa_mode) => { + // Do this first so if it crashes the setting isn't saved :) + global_state + .window + .renderer_mut() + .set_aa_mode(new_aa_mode) + .unwrap(); + global_state.settings.graphics.aa_mode = new_aa_mode; + global_state.settings.save_to_file_warn(); } } } diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 02196ee77a..e2603baada 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -1,5 +1,6 @@ use crate::{ hud::{BarNumbers, CrosshairType, ShortcutNumbers, XpBar}, + render::AaMode, ui::ScaleMode, window::KeyMouse, }; @@ -160,6 +161,7 @@ pub struct GraphicsSettings { pub view_distance: u32, pub max_fps: u32, pub fov: u16, + pub aa_mode: AaMode, } impl Default for GraphicsSettings { @@ -168,6 +170,7 @@ impl Default for GraphicsSettings { view_distance: 5, max_fps: 60, fov: 75, + aa_mode: AaMode::Fxaa, } } } diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index bb03715d9e..a8f8f5fd53 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -15,6 +15,7 @@ pub use widgets::{ image_frame::ImageFrame, image_slider::ImageSlider, ingame::{Ingame, IngameAnchor, Ingameable}, + radio_list::RadioList, toggle_button::ToggleButton, tooltip::{Tooltip, TooltipManager, Tooltipable}, }; diff --git a/voxygen/src/ui/widgets/mod.rs b/voxygen/src/ui/widgets/mod.rs index 3cdc003505..7c26721f56 100644 --- a/voxygen/src/ui/widgets/mod.rs +++ b/voxygen/src/ui/widgets/mod.rs @@ -1,5 +1,6 @@ pub mod image_frame; pub mod image_slider; pub mod ingame; +pub mod radio_list; pub mod toggle_button; pub mod tooltip; diff --git a/voxygen/src/ui/widgets/radio_list.rs b/voxygen/src/ui/widgets/radio_list.rs new file mode 100644 index 0000000000..0479479d5c --- /dev/null +++ b/voxygen/src/ui/widgets/radio_list.rs @@ -0,0 +1,202 @@ +use conrod_core::{ + builder_methods, image, text, + widget::{self, button}, + widget_ids, Color, FontSize, Positionable, Rect, Sizeable, Widget, WidgetCommon, +}; + +#[derive(Clone, WidgetCommon)] +pub struct RadioList<'a, T> { + #[conrod(common_builder)] + common: widget::CommonBuilder, + f_image: button::Image, + t_image: button::Image, + selected: usize, + options_labels: &'a [(&'a T, &'a str)], + label_style: widget::text::Style, + label_spacing: f64, + button_spacing: [f64; 2], + button_dims: [f64; 2], +} + +widget_ids! { + struct Ids { + buttons[], + labels[], + } +} + +pub struct State { + ids: Ids, +} + +impl<'a, T> RadioList<'a, T> { + pub fn new( + selected: usize, + f_image_id: image::Id, + t_image_id: image::Id, + options_labels: &'a [(&'a T, &'a str)], + ) -> Self { + Self { + common: widget::CommonBuilder::default(), + f_image: button::Image { + image_id: f_image_id, + hover_image_id: None, + press_image_id: None, + src_rect: None, + color: button::ImageColor::None, + }, + t_image: button::Image { + image_id: t_image_id, + hover_image_id: None, + press_image_id: None, + src_rect: None, + color: button::ImageColor::None, + }, + selected, + label_style: widget::text::Style::default(), + options_labels, + label_spacing: 10.0, + button_spacing: [5.0, 5.0], + button_dims: [15.0, 15.0], + } + } + + pub fn source_rectangle(mut self, rect: Rect) -> Self { + self.f_image.src_rect = Some(rect); + self.t_image.src_rect = Some(rect); + self + } + + pub fn image_colors(mut self, f_color: Color, t_color: Color) -> Self { + self.f_image.color = button::ImageColor::Normal(f_color); + self.t_image.color = button::ImageColor::Normal(t_color); + self + } + + pub fn image_color_with_feedback(mut self, f_color: Color, t_color: Color) -> Self { + self.f_image.color = button::ImageColor::WithFeedback(f_color); + self.t_image.color = button::ImageColor::WithFeedback(t_color); + self + } + + pub fn hover_images(mut self, f_id: image::Id, t_id: image::Id) -> Self { + self.f_image.hover_image_id = Some(f_id); + self.t_image.hover_image_id = Some(t_id); + self + } + + pub fn press_images(mut self, f_id: image::Id, t_id: image::Id) -> Self { + self.f_image.press_image_id = Some(f_id); + self.t_image.press_image_id = Some(t_id); + self + } + builder_methods! { + pub text_color { label_style.color = Some(Color) } + pub font_size { label_style.font_size = Some(FontSize) } + pub justify { label_style.justify = Some(text::Justify) } + pub line_spacing { label_style.line_spacing = Some(f64) } + pub label_spacing { label_spacing = f64 } + pub button_spacing { button_spacing = [f64; 2] } + pub button_dims { button_dims = [f64; 2] } + } +} + +impl<'a, T> Widget for RadioList<'a, T> { + type State = State; + type Style = (); + type Event = Option<(usize, &'a T)>; + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + } + } + + fn style(&self) -> Self::Style { + () + } + + fn update(self, args: widget::UpdateArgs) -> Self::Event { + let widget::UpdateArgs { + id, + state, + ui, + rect, + .. + } = args; + let Self { + f_image, + t_image, + selected, + options_labels, + label_style, + label_spacing, + button_spacing, + button_dims, + .. + } = self; + + // Ensure we have enough widget ids + let num_items = options_labels.len(); + if state.ids.buttons.len() < num_items || state.ids.labels.len() < num_items { + state.update(|s| { + s.ids + .buttons + .resize(num_items, &mut ui.widget_id_generator()); + s.ids + .labels + .resize(num_items, &mut ui.widget_id_generator()); + }); + } + + // Check if the button was clicked. + // (Can't use `.set().was_clicked()` because we are changing the image after setting the + // widget, which causes flickering since it takes a frame to change after the mouse button + // is lifted). + let current_selection = (0..num_items) + .find(|i| { + ui.widget_input(state.ids.buttons[*i]) + .clicks() + .left() + .count() + % 2 + == 1 + }) + .unwrap_or(selected); + + let (x, y, w, h) = rect.x_y_w_h(); + for i in 0..num_items { + let image = if i == current_selection { + t_image + } else { + f_image + }; + // Button + let mut button = button::Button::image(image.image_id) + .wh(button_dims) + //TODO: implement default width / height functions + .x_y( + x - w / 2.0 + button_spacing[0], + y - h / 2.0 + - i as f64 * (button_dims[1] + button_spacing[1]) + - button_spacing[1], + ) + .parent(id); + button.show = image; + button.set(state.ids.buttons[i], ui); + // Label + widget::Text::new(options_labels[i].1) + .graphics_for(state.ids.buttons[i]) + .parent(id) + .with_style(label_style) + .right_from(state.ids.buttons[i], label_spacing) + .set(state.ids.labels[i], ui); + } + + if current_selection != selected { + Some((current_selection, options_labels[current_selection].0)) + } else { + None + } + } +} diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index a0ccf0154e..5564e86a04 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -225,7 +225,13 @@ impl Window { Ok(Self { events_loop, - renderer: Renderer::new(device, factory, win_color_view, win_depth_view)?, + renderer: Renderer::new( + device, + factory, + win_color_view, + win_depth_view, + settings.graphics.aa_mode, + )?, window, cursor_grabbed: false, pan_sensitivity: settings.gameplay.pan_sensitivity, From 799e9626631bd81612e66fa70ea6ce59f2ce8c65 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 26 Sep 2019 23:40:58 -0400 Subject: [PATCH 3/4] AA mode UI improvements --- voxygen/src/hud/settings_window.rs | 2 ++ voxygen/src/render/renderer.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index bc6a4da576..1fe4289c4b 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -1224,6 +1224,8 @@ impl<'a> Widget for SettingsWindow<'a> { self.imgs.check_checked, &mode_label_list, ) + .hover_images(self.imgs.check_mo, self.imgs.check_checked_mo) + .press_images(self.imgs.check_press, self.imgs.check_press) .down_from(state.ids.aa_mode_text, 8.0) .text_color(TEXT_COLOR) .font_size(12) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index b64888faf0..3597444726 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -821,6 +821,6 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( samples: Some(gfx::state::MultiSample), }, pipe, - )?, // Do some funky things to work around an oddity in gfx's error ownership rules. + )?, }) } From 5ca65a6894f3856e479438b1f545ed627483af99 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 27 Sep 2019 00:23:42 -0400 Subject: [PATCH 4/4] UI tweaks --- voxygen/src/hud/bag.rs | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index e9b18e7f8a..c1ff1c5e79 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -120,26 +120,23 @@ impl<'a> Widget for Bag<'a> { .w_h(61.0 * BAG_SCALE, 9.0 * BAG_SCALE) .bottom_right_with_margins_on(ui.window, 60.0, 5.0) .set(state.ids.bag_bot, ui); + let mid_height = ((inventory.len() + 4) / 5) as f64 * 44.0; + Image::new(self.imgs.bag_mid) + .w_h(61.0 * BAG_SCALE, mid_height) + .up_from(state.ids.bag_bot, 0.0) + .set(state.ids.bag_mid, ui); Image::new(self.imgs.bag_top) .w_h(61.0 * BAG_SCALE, 9.0 * BAG_SCALE) .up_from(state.ids.bag_mid, 0.0) .set(state.ids.bag_top, ui); - Image::new(self.imgs.bag_mid) - .w_h(61.0 * BAG_SCALE, ((inventory.len() + 4) / 5) as f64 * 44.0) - .up_from(state.ids.bag_bot, 0.0) - .set(state.ids.bag_mid, ui); // Alignment for Grid - Rectangle::fill_with( - [54.0 * BAG_SCALE, ((inventory.len() + 4) / 5) as f64 * 44.0], - color::TRANSPARENT, - ) - .top_left_with_margins_on(state.ids.bag_top, 9.0 * BAG_SCALE, 3.0 * BAG_SCALE) - .scroll_kids() - .scroll_kids_vertically() - .set(state.ids.inv_alignment, ui); - // Create available inventory slot widgets + Rectangle::fill_with([54.0 * BAG_SCALE, mid_height], color::TRANSPARENT) + .top_left_with_margins_on(state.ids.bag_mid, 0.0, 3.0 * BAG_SCALE) + .scroll_kids_vertically() + .set(state.ids.inv_alignment, ui); + // Create available inventory slot widgets if state.ids.inv_slots.len() < inventory.len() { state.update(|s| { s.ids @@ -147,7 +144,6 @@ impl<'a> Widget for Bag<'a> { .resize(inventory.len(), &mut ui.widget_id_generator()); }); } - if state.ids.items.len() < inventory.len() { state.update(|s| { s.ids @@ -157,7 +153,6 @@ impl<'a> Widget for Bag<'a> { } // Display inventory contents - for (i, item) in inventory.slots().iter().enumerate() { let x = i % 5; let y = i / 5; @@ -171,16 +166,15 @@ impl<'a> Widget for Bag<'a> { 4.0 + y as f64 * (40.0 + 4.0), 4.0 + x as f64 * (40.0 + 4.0), ) // conrod uses a (y,x) format for placing... - .parent(state.ids.inv_alignment) // Avoids the background overlapping available slots + // (the margin placement functions do this because that is the same order as "top left") .w_h(40.0, 40.0) .image_color(if is_selected { color::WHITE } else { color::DARK_YELLOW - }) - .floating(true); + }); - let slot_widget = if let Some(item) = item { + let slot_widget_clicked = if let Some(item) = item { slot_widget .with_tooltip( self.tooltip_manager, @@ -191,10 +185,11 @@ impl<'a> Widget for Bag<'a> { .set(state.ids.inv_slots[i], ui) } else { slot_widget.set(state.ids.inv_slots[i], ui) - }; + } + .was_clicked(); // Item - if slot_widget.was_clicked() { + if slot_widget_clicked { let selected_slot = match state.selected_slot { Some(a) => { if a == i { @@ -227,7 +222,6 @@ impl<'a> Widget for Bag<'a> { } // Close button - if Button::image(self.imgs.close_button) .w_h(28.0, 28.0) .hover_image(self.imgs.close_button_hover)