diff --git a/assets/voxygen/shaders/fluid-frag.glsl b/assets/voxygen/shaders/fluid-frag.glsl index f9cc065b1f..12393caeb7 100644 --- a/assets/voxygen/shaders/fluid-frag.glsl +++ b/assets/voxygen/shaders/fluid-frag.glsl @@ -4,7 +4,7 @@ #include in vec3 f_pos; -flat in vec3 f_norm; +flat in uint f_pos_norm; in vec3 f_col; in float f_light; @@ -14,6 +14,8 @@ uniform u_locals { float load_time; }; +uniform sampler2D t_waves; + out vec4 tgt_color; #include @@ -26,6 +28,16 @@ vec3 warp_normal(vec3 norm, vec3 pos, float time) { } void main() { + // First 3 normals are negative, next 3 are positive + vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); + + // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. + uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // Increase array access by 3 to access positive values + uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // Use an array to avoid conditional branching + vec3 f_norm = normals[norm_axis + norm_dir]; + /* // Round the position to the nearest triangular grid cell vec3 hex_pos = f_pos * 2.0; @@ -36,7 +48,25 @@ void main() { hex_pos = floor(hex_pos); */ - vec3 norm = warp_normal(f_norm, f_pos, tick.x); + vec3 b_norm; + if (f_norm.z > 0.0) { + b_norm = vec3(1, 0, 0); + } else if (f_norm.x > 0.0) { + b_norm = vec3(0, 1, 0); + } else { + b_norm = vec3(0, 0, 1); + } + vec3 c_norm = cross(f_norm, b_norm); + + vec3 nmap = normalize( + (srgb_to_linear(texture(t_waves, fract(f_pos.xy * 0.3 + tick.x * 0.04)).rgb) - 0.0) * 0.05 + + (srgb_to_linear(texture(t_waves, fract(f_pos.xy * 0.1 - tick.x * 0.08)).rgb) - 0.0) * 0.1 + + (srgb_to_linear(texture(t_waves, fract(-f_pos.yx * 0.06 - tick.x * 0.1)).rgb) - 0.0) * 0.1 + + (srgb_to_linear(texture(t_waves, fract(-f_pos.yx * 0.03 - tick.x * 0.01)).rgb) - 0.0) * 0.2 + + vec3(0, 0, 0.0) + ); + + vec3 norm = f_norm * nmap.z + b_norm * nmap.x + c_norm * nmap.y; vec3 light, diffuse_light, ambient_light; get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); @@ -46,7 +76,7 @@ void main() { vec3 point_light = light_at(f_pos, f_norm); light += point_light; diffuse_light += point_light; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true); @@ -57,10 +87,11 @@ void main() { reflect_ray_dir.z = max(reflect_ray_dir.z, 0.05); vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, false) * f_light; + //reflect_color = vec3(reflect_color.r + reflect_color.g + reflect_color.b) / 3.0; // 0 = 100% reflection, 1 = translucent water - float passthrough = pow(dot(faceforward(norm, norm, cam_to_frag), -cam_to_frag), 1.0); + float passthrough = pow(dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag), 0.3); - vec4 color = mix(vec4(reflect_color, 1.0), vec4(surf_color, 3.0 / (1.0 + diffuse_light * 2.0)), passthrough); + vec4 color = mix(vec4(reflect_color * 2.0, 1.0), vec4(surf_color, 4.0 / (1.0 + diffuse_light * 2.0)), passthrough); tgt_color = mix(color, vec4(fog_color, 0.0), fog_level); } diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl index 97381567e6..07834690e9 100644 --- a/assets/voxygen/shaders/fluid-vert.glsl +++ b/assets/voxygen/shaders/fluid-vert.glsl @@ -13,13 +13,11 @@ uniform u_locals { }; out vec3 f_pos; +flat out uint f_pos_norm; flat out vec3 f_norm; out vec3 f_col; out float f_light; -// First 3 normals are negative, next 3 are positive -vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); - void main() { f_pos = vec3( float((v_pos_norm >> 0) & 0x00FFu), @@ -27,23 +25,16 @@ void main() { float((v_pos_norm >> 16) & 0x1FFFu) ) + model_offs; - // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - uint norm_axis = (v_pos_norm >> 30) & 0x3u; - - // Increase array access by 3 to access positive values - uint norm_dir = ((v_pos_norm >> 29) & 0x1u) * 3u; - - // Use an array to avoid conditional branching - f_norm = normals[norm_axis + norm_dir]; - - f_col = srgb_to_linear(vec3( + f_col = vec3( float((v_col_light >> 8) & 0xFFu), float((v_col_light >> 16) & 0xFFu), float((v_col_light >> 24) & 0xFFu) - ) / 255.0); + ) / 255.0; f_light = float(v_col_light & 0xFFu) / 255.0; + f_pos_norm = v_pos_norm; + gl_Position = proj_mat * view_mat * diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index 2c4ca268be..6eb3ff8602 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -31,11 +31,11 @@ vec3 get_sun_dir(float time_of_day) { const float PERSISTENT_AMBIANCE = 0.1; float get_sun_brightness(vec3 sun_dir) { - return max(-sun_dir.z + 0.6, 0.0); + return max(-sun_dir.z + 0.6, 0.0) * 0.9; } void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light, float diffusion) { - const float SUN_AMBIANCE = 0.0; + const float SUN_AMBIANCE = 0.1; vec3 sun_dir = get_sun_dir(time_of_day); diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl index 3b19fa198c..7727afe185 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -3,7 +3,7 @@ #include in vec3 f_pos; -flat in vec3 f_norm; +flat in uint f_pos_norm; in vec3 f_col; in float f_light; @@ -19,6 +19,16 @@ out vec4 tgt_color; #include void main() { + // First 3 normals are negative, next 3 are positive + vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); + + // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. + uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // Increase array access by 3 to access positive values + uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // Use an array to avoid conditional branching + vec3 f_norm = normals[norm_axis + norm_dir]; + vec3 light, diffuse_light, ambient_light; get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); float point_shadow = shadow_at(f_pos, f_norm); @@ -27,7 +37,7 @@ void main() { vec3 point_light = light_at(f_pos, f_norm); light += point_light; diffuse_light += point_light; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, true); diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl index 4ce800cba1..61ef233cd2 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -13,13 +13,10 @@ uniform u_locals { }; out vec3 f_pos; -flat out vec3 f_norm; +flat out uint f_pos_norm; out vec3 f_col; out float f_light; -// First 3 normals are negative, next 3 are positive -vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); - void main() { f_pos = vec3( float((v_pos_norm >> 0) & 0x00FFu), @@ -29,23 +26,16 @@ void main() { f_pos.z *= min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0); - // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - uint norm_axis = (v_pos_norm >> 30) & 0x3u; - - // Increase array access by 3 to access positive values - uint norm_dir = ((v_pos_norm >> 29) & 0x1u) * 3u; - - // Use an array to avoid conditional branching - f_norm = normals[norm_axis + norm_dir]; - - f_col = srgb_to_linear(vec3( + f_col = vec3( float((v_col_light >> 8) & 0xFFu), float((v_col_light >> 16) & 0xFFu), float((v_col_light >> 24) & 0xFFu) - ) / 255.0); + ) / 255.0; f_light = float(v_col_light & 0xFFu) / 255.0; + f_pos_norm = v_pos_norm; + gl_Position = proj_mat * view_mat * diff --git a/assets/voxygen/texture/waves.png b/assets/voxygen/texture/waves.png new file mode 100644 index 0000000000..3083b00a86 --- /dev/null +++ b/assets/voxygen/texture/waves.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9beac98bc31b98390c7d0f3bba959831d7734518d521d4f2fe10d96d0756ea66 +size 15547 diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index a6cc9533e5..aa871ce973 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -99,11 +99,14 @@ impl<'a> System<'a> for Sys { if character.movement.is_roll() { vel.0 = Vec3::new(0.0, 0.0, vel.0.z) - + controller - .move_dir - .try_normalized() - .unwrap_or(Vec2::from(vel.0).try_normalized().unwrap_or_default()) - * ROLL_SPEED + + (vel.0 * Vec3::new(1.0, 1.0, 0.0) + + 1.5 + * controller + .move_dir + .try_normalized() + .unwrap_or(Vec2::from(vel.0).try_normalized().unwrap_or_default())) + .normalized() + * ROLL_SPEED; } if character.action.is_block() || character.action.is_attack() { vel.0 += Vec2::broadcast(dt.0) diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs index 89c05bd882..e387437da1 100644 --- a/voxygen/src/render/pipelines/fluid.rs +++ b/voxygen/src/render/pipelines/fluid.rs @@ -29,6 +29,8 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + waves: gfx::TextureSampler<[f32; 4]> = "t_waves", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), tgt_depth: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 3597444726..94c2330915 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -354,8 +354,10 @@ impl Renderer { pub fn create_texture( &mut self, image: &image::DynamicImage, + filter_method: Option, + wrap_mode: Option, ) -> Result, RenderError> { - Texture::new(&mut self.factory, image) + Texture::new(&mut self.factory, image, filter_method, wrap_mode) } /// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the specified dimensions. @@ -518,6 +520,7 @@ impl Renderer { locals: &Consts, lights: &Consts, shadows: &Consts, + waves: &Texture, ) { self.encoder.draw( &gfx::Slice { @@ -534,6 +537,7 @@ impl Renderer { globals: globals.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), + waves: (waves.srv.clone(), waves.sampler.clone()), tgt_color: self.tgt_color_view.clone(), tgt_depth: self.tgt_depth_view.clone(), }, diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index e4c528efbf..66d0af88c4 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -24,6 +24,8 @@ impl Texture

{ pub fn new( factory: &mut gfx_backend::Factory, image: &DynamicImage, + filter_method: Option, + wrap_mode: Option, ) -> Result { let (tex, srv) = factory .create_texture_immutable_u8::( @@ -41,12 +43,13 @@ impl Texture

{ tex, srv, sampler: factory.create_sampler(gfx::texture::SamplerInfo::new( - gfx::texture::FilterMethod::Scale, - gfx::texture::WrapMode::Clamp, + filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), + wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), )), _phantom: PhantomData, }) } + pub fn new_dynamic( factory: &mut gfx_backend::Factory, width: u16, diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index a7a42f60a5..1567f65f57 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -2,7 +2,7 @@ use crate::{ mesh::Meshable, render::{ Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow, - SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline, + SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline, Texture, }, }; @@ -10,7 +10,7 @@ use client::Client; use common::{ assets, figure::Segment, - terrain::{Block, BlockKind}, + terrain::{Block, BlockKind, TerrainChunk}, vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox}, volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError}, }; @@ -21,11 +21,11 @@ use hashbrown::HashMap; use std::{f32, fmt::Debug, i32, marker::PhantomData, ops::Mul, time::Duration}; use vek::*; -struct TerrainChunk { +struct TerrainChunkData { // GPU data load_time: f32, opaque_model: Model, - fluid_model: Model, + fluid_model: Option>, sprite_instances: HashMap<(BlockKind, usize), Instances>, locals: Consts, @@ -198,7 +198,7 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( } pub struct Terrain { - chunks: HashMap, TerrainChunk>, + chunks: HashMap, TerrainChunkData>, // The mpsc sender and receiver used for talking to meshing worker threads. // We keep the sender component for no reason other than to clone it and send it to new workers. @@ -208,6 +208,7 @@ pub struct Terrain { // GPU data sprite_models: HashMap<(BlockKind, usize), Model>, + waves: Texture, phantom: PhantomData, } @@ -656,6 +657,13 @@ impl Terrain { ] .into_iter() .collect(), + waves: renderer + .create_texture( + &assets::load_expect("voxygen.texture.waves"), + Some(gfx::texture::FilterMethod::Bilinear), + Some(gfx::texture::WrapMode::Tile), + ) + .expect("Failed to create wave texture"), phantom: PhantomData, } } @@ -862,14 +870,20 @@ impl Terrain { .unwrap_or(current_time as f32); self.chunks.insert( response.pos, - TerrainChunk { + TerrainChunkData { load_time, opaque_model: renderer .create_model(&response.opaque_mesh) .expect("Failed to upload chunk mesh to the GPU!"), - fluid_model: renderer - .create_model(&response.fluid_mesh) - .expect("Failed to upload chunk mesh to the GPU!"), + fluid_model: if response.fluid_mesh.vertices().len() > 0 { + Some( + renderer + .create_model(&response.fluid_mesh) + .expect("Failed to upload chunk mesh to the GPU!"), + ) + } else { + None + }, sprite_instances: response .sprite_instances .into_iter() @@ -954,8 +968,25 @@ impl Terrain { shadows: &Consts, focus_pos: Vec3, ) { + let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { + (e as i32).div_euclid(sz as i32) + }); + + let chunks = &self.chunks; + let chunk_iter = Spiral2d::new() + .scan(0, |n, rpos| { + if *n >= chunks.len() { + None + } else { + *n += 1; + let pos = focus_chunk + rpos; + Some(chunks.get(&pos).map(|c| (pos, c))) + } + }) + .filter_map(|x| x); + // Opaque - for (_, chunk) in &self.chunks { + for (_, chunk) in chunk_iter.clone() { if chunk.visible { renderer.render_terrain_chunk( &chunk.opaque_model, @@ -968,7 +999,7 @@ impl Terrain { } // Terrain sprites - for (pos, chunk) in &self.chunks { + for (pos, chunk) in chunk_iter.clone() { if chunk.visible { const SPRITE_RENDER_DISTANCE: f32 = 128.0; @@ -991,16 +1022,53 @@ impl Terrain { } // Translucent - for (_, chunk) in &self.chunks { - if chunk.visible { - renderer.render_fluid_chunk( - &chunk.fluid_model, - globals, - &chunk.locals, - lights, - shadows, - ); - } - } + chunk_iter + .clone() + .filter(|(_, chunk)| chunk.visible) + .filter_map(|(_, chunk)| { + chunk + .fluid_model + .as_ref() + .map(|model| (model, &chunk.locals)) + }) + .for_each(|(model, locals)| { + renderer.render_fluid_chunk(model, globals, locals, lights, shadows, &self.waves) + }); + } +} + +#[derive(Clone)] +struct Spiral2d { + layer: i32, + i: i32, +} + +impl Spiral2d { + pub fn new() -> Self { + Self { layer: 0, i: 0 } + } +} + +impl Iterator for Spiral2d { + type Item = Vec2; + + fn next(&mut self) -> Option { + let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1); + if self.i >= layer_size { + self.layer += 1; + self.i = 0; + } + let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1); + + let pos = Vec2::new( + -self.layer + (self.i - (layer_size / 4) * 0).max(0).min(self.layer * 2) + - (self.i - (layer_size / 4) * 2).max(0).min(self.layer * 2), + -self.layer + (self.i - (layer_size / 4) * 1).max(0).min(self.layer * 2) + - (self.i - (layer_size / 4) * 3).max(0).min(self.layer * 2), + ); + + self.i += 1; + + Some(pos) } }