diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 53efa597af..6bd65b7463 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -70,7 +70,7 @@ uniform u_bones { BoneData bones[16]; }; -#include +#include #include #include @@ -87,7 +87,9 @@ void main() { // float f_ao = f_col_light.a; float f_ao, f_glow; - vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_ao, f_glow); + uint material = 0xFFu; + vec3 f_col = greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_ao, f_glow, material); + // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // vec3 my_chunk_pos = (vec3((uvec3(f_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; @@ -161,6 +163,14 @@ void main() { vec3 k_d = vec3(1.0); vec3 k_s = vec3(R_s); + // This is a silly hack. It's not true reflectance (see below for that), but gives the desired + // effect without breaking the entire lighting model until we come up with a better way of doing + // reflectivity that accounts for physical surroundings like the ground + if ((material & (1u << 1u)) > 0u) { + vec3 reflect_ray_dir = reflect(cam_to_frag, f_norm); + surf_color *= dot(vec3(1.0) - abs(fract(reflect_ray_dir * 3.5) * 2.0 - 1.0) * 0.85, vec3(1)); + } + vec3 emitted_light, reflected_light; // Make voxel shadows block the sun and moon @@ -184,10 +194,17 @@ void main() { float ao = f_ao * sqrt(f_ao);//0.25 + f_ao * 0.75; ///*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + // For now, just make glowing material light be the same colour as the surface + // TODO: Add a way to control this better outside the shaders + if ((material & (1u << 0u)) > 0u) { + emitted_light += 1000 * surf_color; + } + float glow_mag = length(model_glow.xyz); vec3 glow = pow(model_glow.w, 2) * 4 * glow_light(f_pos) * (max(dot(f_norm, model_glow.xyz / glow_mag) * 0.5 + 0.5, 0.0) + max(1.0 - glow_mag, 0.0)); + emitted_light += glow; reflected_light *= ao; @@ -206,7 +223,20 @@ void main() { // diffuse_light += point_light; // reflected_light += point_light; // vec3 surf_color = illuminate(srgb_to_linear(highlight_col.rgb * f_col), light, diffuse_light, ambient_light); - surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light) * highlight_col.rgb; + + float reflectance = 0.0; + // TODO: Do reflectance properly like this later + vec3 reflect_color = vec3(0); + /* + if ((material & (1u << 1u)) > 0u && false) { + vec3 reflect_ray_dir = reflect(cam_to_frag, f_norm); + reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true); + reflect_color = get_cloud_color(reflect_color, reflect_ray_dir, cam_pos.xyz, time_of_day.x, 100000.0, 0.25); + reflectance = 1.0; + } + */ + + surf_color = illuminate(max_light, view_dir, mix(surf_color * emitted_light, reflect_color, reflectance), mix(surf_color * reflected_light, reflect_color, reflectance)) * highlight_col.rgb; // if ((flags & 1) == 1 && int(cam_mode) == 1) { // float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index 4ba6ddf5f9..c978b4febb 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -618,7 +618,7 @@ vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_a //} //#endif -vec3 greedy_extract_col_light_glow(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) { +vec3 greedy_extract_col_light_attr(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out uint f_attr) { uvec4 f_col_light = uvec4(texelFetch(t_col_light, ivec2(f_uv_pos), 0) * 255); vec3 f_col = vec3( float(((f_col_light.r & 0x7u) << 1u) | (f_col_light.b & 0xF0u)), @@ -640,5 +640,11 @@ vec3 greedy_extract_col_light_glow(sampler2D t_col_light, vec2 f_uv_pos, out flo f_light = light.x / 31.0; f_glow = light.y / 31.0; + f_attr = f_col_light.g >> 3u; return srgb_to_linear(f_col); } + +vec3 greedy_extract_col_light_glow(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) { + uint f_attr; + return greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_light, f_glow, f_attr); +} diff --git a/assets/voxygen/voxel/weapon/staff/firestaff_cultist.vox b/assets/voxygen/voxel/weapon/staff/firestaff_cultist.vox index 4e61bb3b4e..5ed69240ae 100644 Binary files a/assets/voxygen/voxel/weapon/staff/firestaff_cultist.vox and b/assets/voxygen/voxel/weapon/staff/firestaff_cultist.vox differ diff --git a/common/src/figure/cell.rs b/common/src/figure/cell.rs index a6ada431a6..b3473d2fe9 100644 --- a/common/src/figure/cell.rs +++ b/common/src/figure/cell.rs @@ -1,22 +1,60 @@ use crate::vol::Vox; use vek::*; +pub(super) const GLOWY: u8 = 1 << 0; +pub(super) const SHINY: u8 = 1 << 1; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(packed)] +pub struct CellData { + pub col: Rgb, + pub attr: u8, // 0 = glowy, 1 = shiny +} + +impl Default for CellData { + fn default() -> Self { + Self { + col: Rgb::broadcast(255), + attr: 0, + } + } +} + /// A type representing a single voxel in a figure. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Cell { - Filled([u8; 3]), + Filled(CellData), Empty, } impl Cell { - pub fn new(rgb: Rgb) -> Self { Cell::Filled(rgb.into_array()) } + pub fn new(col: Rgb, glowy: bool, shiny: bool) -> Self { + Cell::Filled(CellData { + col, + attr: glowy as u8 * GLOWY + shiny as u8 * SHINY, + }) + } pub fn get_color(&self) -> Option> { match self { - Cell::Filled(col) => Some(Rgb::from(*col)), + Cell::Filled(data) => Some(data.col), Cell::Empty => None, } } + + pub fn is_glowy(&self) -> bool { + match self { + Cell::Filled(data) => data.attr & GLOWY != 0, + Cell::Empty => false, + } + } + + pub fn is_shiny(&self) -> bool { + match self { + Cell::Filled(data) => data.attr & SHINY != 0, + Cell::Empty => false, + } + } } impl Vox for Cell { diff --git a/common/src/figure/mat_cell.rs b/common/src/figure/mat_cell.rs index 0c066d3681..a6a0137643 100644 --- a/common/src/figure/mat_cell.rs +++ b/common/src/figure/mat_cell.rs @@ -1,5 +1,5 @@ +use super::cell::CellData; use crate::vol::Vox; -use vek::*; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Material { @@ -19,7 +19,7 @@ pub enum Material { pub enum MatCell { None, Mat(Material), - Normal(Rgb), + Normal(CellData), } impl Vox for MatCell { diff --git a/common/src/figure/mod.rs b/common/src/figure/mod.rs index 0aa75d50eb..2cff2fe016 100644 --- a/common/src/figure/mod.rs +++ b/common/src/figure/mod.rs @@ -3,8 +3,12 @@ pub mod mat_cell; pub use mat_cell::Material; // Reexport -pub use self::{cell::Cell, mat_cell::MatCell}; +pub use self::{ + cell::{Cell, CellData}, + mat_cell::MatCell, +}; +use self::cell::{GLOWY, SHINY}; use crate::{ vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol}, volumes::dyna::Dyna, @@ -60,7 +64,11 @@ impl Segment { voxel.z, ) .map(i32::from), - Cell::new(color), + Cell::new( + color, + (13..16).contains(&voxel.i), // Glowy + (8..13).contains(&voxel.i), // Shiny + ), ) .unwrap(); }; @@ -85,7 +93,10 @@ impl Segment { /// Transform cell colors pub fn map_rgb(self, transform: impl Fn(Rgb) -> Rgb) -> Self { - self.map(|cell| cell.get_color().map(|rgb| Cell::new(transform(rgb)))) + self.map(|cell| { + cell.get_color() + .map(|rgb| Cell::new(transform(rgb), cell.is_glowy(), cell.is_shiny())) + }) } } @@ -146,12 +157,15 @@ impl MatSegment { pub fn to_segment(&self, map: impl Fn(Material) -> Rgb) -> Segment { let mut vol = Dyna::filled(self.size(), Cell::empty(), ()); for (pos, vox) in self.full_vol_iter() { - let rgb = match vox { + let data = match vox { MatCell::None => continue, - MatCell::Mat(mat) => map(*mat), - MatCell::Normal(rgb) => *rgb, + MatCell::Mat(mat) => CellData { + col: map(*mat), + attr: 0, + }, + MatCell::Normal(data) => *data, }; - vol.set(pos, Cell::new(rgb)).unwrap(); + vol.set(pos, Cell::Filled(data)).unwrap(); } vol } @@ -170,11 +184,15 @@ impl MatSegment { /// Transform cell colors pub fn map_rgb(self, transform: impl Fn(Rgb) -> Rgb) -> Self { self.map(|cell| match cell { - MatCell::Normal(rgb) => Some(MatCell::Normal(transform(rgb))), + MatCell::Normal(data) => Some(MatCell::Normal(CellData { + col: transform(data.col), + ..data + })), _ => None, }) } + #[allow(clippy::identity_op)] pub fn from_vox(dot_vox_data: &DotVoxData, flipped: bool) -> Self { if let Some(model) = dot_vox_data.models.get(0) { let palette = dot_vox_data @@ -204,7 +222,12 @@ impl MatSegment { .get(index as usize) .copied() .unwrap_or_else(|| Rgb::broadcast(0)); - MatCell::Normal(color) + MatCell::Normal(CellData { + col: color, + attr: 0 + | ((13..16).contains(&index) as u8 * GLOWY) + | ((8..13).contains(&index) as u8 * SHINY), + }) }, }; diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 50ceaac0cf..185d9b0998 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -214,6 +214,6 @@ fn graceful_load_segment_no_skin(specifier: &str) -> Arc { MatCell::Mat(_) => Some(MatCell::None), MatCell::Normal(_) => None, }) - .to_segment(|_| Rgb::broadcast(255)); + .to_segment(|_| Default::default()); Arc::new(seg) } diff --git a/voxygen/src/mesh/greedy.rs b/voxygen/src/mesh/greedy.rs index 72b07968c2..90d1b3e764 100644 --- a/voxygen/src/mesh/greedy.rs +++ b/voxygen/src/mesh/greedy.rs @@ -11,7 +11,7 @@ type TodoRect = ( Vec3, ); -pub struct GreedyConfig { +pub struct GreedyConfig { pub data: D, /// The minimum position to mesh, in the coordinate system used /// for queries against the volume. @@ -63,6 +63,9 @@ pub struct GreedyConfig { /// world space, the normal facing out frmo the rectangle in world /// space, and meta information common to every voxel in this rectangle. pub push_quad: FP, + /// Create a texel (in the texture atlas) that corresponds to a face with + /// the given properties. + pub make_face_texel: FT, } /// A suspended greedy mesh, with enough information to recover color data. @@ -143,9 +146,9 @@ impl<'a> GreedyMesh<'a> { /// Returns an estimate of the bounds of the current meshed model. /// /// For more information on the config parameter, see [GreedyConfig]. - pub fn push( + pub fn push( &mut self, - config: GreedyConfig, + config: GreedyConfig, ) where FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, FG: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, @@ -153,6 +156,7 @@ impl<'a> GreedyMesh<'a> { FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), + FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType + 'a, { span!(_guard, "push", "GreedyMesh::push"); let cont = greedy_mesh( @@ -190,7 +194,7 @@ impl<'a> GreedyMesh<'a> { pub fn max_size(&self) -> guillotiere::Size { self.max_size } } -fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FC, FO, FS, FP>( +fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FC, FO, FS, FP, FT>( atlas: &mut guillotiere::SimpleAtlasAllocator, col_lights_size: &mut Vec2, max_size: guillotiere::Size, @@ -205,7 +209,8 @@ fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FC, FO, FS, FP>( get_opacity, mut should_draw, mut push_quad, - }: GreedyConfig, + make_face_texel, + }: GreedyConfig, ) -> Box> where FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, @@ -214,6 +219,7 @@ where FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), + FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType + 'a, { span!(_guard, "greedy_mesh"); // TODO: Collect information to see if we can choose a good value here. @@ -353,7 +359,7 @@ where get_glow, get_color, get_opacity, - TerrainVertex::make_col_light, + make_face_texel, ); }) } @@ -509,7 +515,7 @@ fn draw_col_lights( mut get_glow: impl FnMut(&mut D, Vec3) -> f32, mut get_color: impl FnMut(&mut D, Vec3) -> Rgb, mut get_opacity: impl FnMut(&mut D, Vec3) -> bool, - mut make_col_light: impl FnMut(u8, u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType, + mut make_face_texel: impl FnMut(&mut D, Vec3, u8, u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType, ) { todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| { // NOTE: Conversions are safe because width, height, and offset must be @@ -586,7 +592,7 @@ fn draw_col_lights( let col = get_color(data, pos); let light = (darkness * 31.5) as u8; let glow = (glowiness * 31.5) as u8; - *col_light = make_col_light(light, glow, col); + *col_light = make_face_texel(data, pos, light, glow, col); }); }); }); diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index b240aaf756..e48a4ea756 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -116,6 +116,13 @@ where |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), )); }, + make_face_texel: |vol: &mut V, pos, light, _, col| { + let (glowy, shiny) = vol + .get(pos) + .map(|c| (c.is_glowy(), c.is_shiny())) + .unwrap_or_default(); + TerrainVertex::make_col_light_figure(light, glowy, shiny, col) + }, }); let bounds = math::Aabb { // NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16. @@ -219,6 +226,9 @@ where |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), )); }, + make_face_texel: |_: &mut V, _, light, glow, col| { + TerrainVertex::make_col_light(light, glow, col) + }, }); (Mesh::new(), Mesh::new(), Mesh::new(), ()) @@ -315,6 +325,9 @@ where |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), )); }, + make_face_texel: |_: &mut V, _, light, glow, col| { + TerrainVertex::make_col_light(light, glow, col) + }, }); (opaque_mesh, Mesh::new(), Mesh::new(), ()) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index de9bed8cc3..177bd58e18 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -439,6 +439,9 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug + 'static> )); }, }, + make_face_texel: |_: &mut (), _, light, glow, col| { + TerrainVertex::make_col_light(light, glow, col) + }, }); let min_bounds = mesh_delta; diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index 9f9290aa49..0395f03f1f 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -133,6 +133,24 @@ impl Vertex { col.g, // Green is lucky, it remains unscathed ] } + + #[allow(clippy::identity_op)] + pub fn make_col_light_figure( + // 0 to 31 + light: u8, + glowy: bool, + shiny: bool, + col: Rgb, + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { + let attr = 0 | ((glowy as u8) << 0) | ((shiny as u8) << 1); + [ + (light.min(31) << 3) | ((col.r >> 1) & 0b111), + (attr.min(31) << 3) | ((col.b >> 1) & 0b111), + (col.r & 0b11110000) | (col.b >> 4), + col.g, // Green is lucky, it remains unscathed + ] + } } impl Locals {