Merge branch 'zesterer/better-rain' into 'master'

Better rain

See merge request veloren/veloren!3464
This commit is contained in:
Isse 2022-07-13 23:25:33 +00:00
commit 1e3b5383cf
27 changed files with 338 additions and 97 deletions

View File

@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Slider for ambience volume
- Weather generated on server is sent to clients, and seen on clients as rain/clouds.
- Updated Brazilian Portuguese Translation
- Lightning storms
### Changed
@ -52,7 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Changed module component modifier costs to the following scheme, based on base material: 1 -> 2 -> 5 -> 10 -> 15 -> 25
- Damage from the same source dealt in the same tick will now be grouped up.
- Critical hits are now shown differently in the damage numbers.
- Fall damage and some (extra) buffs/debuffs now show up in the damage numbers.
- Fall damage and some (extra) buffs/debuffs now show up in the damage numbers.
- Optimized sprite processing decreasing the startup time of voxygen (and long freezes when trying
to enter the world when this hasn't finished).
- Metadata added to music files. Listen to the soundtrack more easily!
@ -70,7 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed an error where '{amount} Exp' floater did not use existing localizations
- Fix villagers seeing cultists and familiar enemies through objects.
- Menacing agents are now less spammy with their menacing messages
- Fixed the title screen FPS cap not applying when the background FPS limit was set higher than 60 FPS
- Fixed the title screen FPS cap not applying when the background FPS limit was set higher than 60 FPS
- Fixed an issue where the hurt animation would "jump" whenever you lost/gained health.
- Fixed a bug where multiple damage sources in the same tick would show up as a singular attack.
- Fixed an issue where, if the same amount of healing and damage was received in the same tick, nothing would be shown.

View File

@ -1169,5 +1169,11 @@
],
threshold: 1.0,
),
Lightning: (
files: [
"voxygen.audio.sfx.ambient.lightning_1",
],
threshold: 1.0,
),
}
)

BIN
assets/voxygen/audio/sfx/ambient/lightning_1.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -82,6 +82,47 @@ void main() {
#if (CLOUD_MODE == CLOUD_MODE_NONE)
color.rgb = apply_point_glow(cam_pos.xyz + focus_off.xyz, dir, dist, color.rgb);
#elif (0 == 0)
if (medium.x == MEDIUM_AIR && rain_density > 0.001) {
vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
vec3 adjusted_dir = (vec4(dir, 0) * rain_dir_mat).xyz;
vec2 dir2d = adjusted_dir.xy;
vec3 rorigin = cam_pos.xyz + focus_off.xyz + 0.5;
vec3 rpos = vec3(0.0);
float t = 0.0;
const float PLANCK = 0.01;
while (true) {
float scale = min(pow(2, ceil(t / 2.0)), 32);
vec2 deltas = (step(vec2(0), dir2d) - fract(rpos.xy / scale + 100.0)) / dir2d;
float jump = max(min(deltas.x, deltas.y) * scale, PLANCK);
t += jump;
if (t >= 64.0) { break; }
rpos = rorigin + adjusted_dir * t;
vec2 diff = abs(round(rpos.xy) - rpos.xy);
vec3 wall_pos = vec3((diff.x > diff.y) ? rpos.xy : rpos.yx, rpos.z + integrated_rain_vel);
wall_pos.xz *= vec2(4, 0.3);
wall_pos.z += hash_two(uvec2(wall_pos.xy + vec2(0, 0.5)));
float depth_adjust = fract(hash_two(uvec2(wall_pos.xz) + 500u));
float wpos_dist = t - jump * depth_adjust;
vec3 wpos = cam_pos.xyz + dir * wpos_dist;
if (wpos_dist > dist) { break; }
if (length((fract(wall_pos.xz) - 0.5)) < 0.1 + pow(max(0.0, wpos_dist - (dist - 0.25)) / 0.25, 4.0) * 0.2) {
float density = rain_density * rain_occlusion_at(wpos);
if (fract(hash_two(uvec2(wall_pos.xz) + 1000u)) >= density) { continue; }
float alpha = 0.5 * clamp((wpos_dist - 1.0) * 0.5, 0.0, 1.0);
float light = dot(color.rgb, vec3(1)) + 0.05 + (get_sun_brightness() + get_moon_brightness()) * 0.2;
color.rgb = mix(color.rgb, vec3(0.3, 0.35, 0.5) * light, alpha);
}
}
}
#else
vec3 old_color = color.rgb;
@ -92,17 +133,17 @@ void main() {
float z = (-1 / (abs(adjusted_dir.z) - 1) - 1) * sign(adjusted_dir.z);
// normalize xy to get a 2d direction
vec2 dir_2d = normalize(adjusted_dir.xy);
// sort of map cylinder around the camera to 2d grid
// sort of map cylinder around the camera to 2d grid
vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z);
// compute camera position in the world
vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
// Rain density is now only based on the cameras current position.
// Rain density is now only based on the cameras current position.
// This could be affected by a setting where rain_density_at is instead
// called each iteration of the loop. With the current implementation
// of rain_dir this has issues with being in a place where it doesn't rain
// and seeing rain.
// and seeing rain.
float rain_density = rain_density * 1.0;
if (medium.x == MEDIUM_AIR && rain_density > 0.0) {
float rain_dist = 50.0;
@ -130,7 +171,7 @@ void main() {
);
float dist_to_rain = drop_depth / length(dir.xy);
vec3 rpos = dir * dist_to_rain;
vec3 rpos = dir * dist_to_rain;
if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) {
continue;
}

View File

@ -164,6 +164,17 @@ void main() {
vec3 surf_color = /*srgb_to_linear*/f_col;
float alpha = 1.0;
const float n2 = 1.5;
// 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 * 1.5) * 2.0 - 1.0) * 0.85, vec3(1));
alpha = 0.1;
}
const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2);
const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2);
const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2);
@ -174,14 +185,6 @@ 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 * 1.5) * 2.0 - 1.0) * 0.85, vec3(1));
}
vec3 emitted_light, reflected_light;
// Make voxel shadows block the sun and moon

View File

@ -242,7 +242,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
int i;
#if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
#ifdef EXPERIMENTAL_RAINBOWS
#ifndef EXPERIMENTAL_NORAINBOWS
// TODO: Make it a double rainbow
float rainbow_t = (0.7 - dot(sun_dir.xyz, dir)) * 8 / 0.05;
int rainbow_c = int(floor(rainbow_t));
@ -285,7 +285,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
// Rainbow
#if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
#ifdef EXPERIMENTAL_RAINBOWS
#ifndef EXPERIMENTAL_NORAINBOWS
if (rainbow_c >= 0 && rainbow_c < 8) {
vec3 colors[9] = {
surf_color,
@ -300,7 +300,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
};
float h = max(0.0, min(pos.z, 900.0 - pos.z) / 450.0);
float rain = rain_density_at(pos.xy) * pow(h, 0.1);
float sun = sun_access * get_sun_brightness();
float energy = pow(rain * sun * min(cdist / 500.0, 1.0), 2.0) * 0.4;

View File

@ -19,6 +19,7 @@ layout(std140, set = 0, binding = 0) uniform u_globals {
uvec4 medium;
ivec4 select_pos;
vec4 gamma_exposure;
vec4 last_lightning;
float ambiance;
// 0 - FirstPerson
// 1 - ThirdPerson

View File

@ -1,6 +1,49 @@
#ifndef POINT_GLOW_GLSL
#define POINT_GLOW_GLSL
#include "sky.glsl"
#ifdef POINT_GLOW_FACTOR
void apply_point_glow_light(Light L, vec3 wpos, vec3 dir, float max_dist, inout vec3 color) {
vec3 light_pos = L.light_pos.xyz;
// Project light_pos to dir line
float t = max(dot(light_pos - wpos, dir), 0);
vec3 nearest = wpos + dir * min(t, max_dist);
vec3 difference = light_pos - nearest;
float distance_2 = dot(difference, difference);
//if (distance_2 > 100000.0) {
// return;
//}
#if (CLOUD_MODE >= CLOUD_MODE_HIGH)
vec3 _unused;
float unused2;
float spread = 1.0 / (1.0 + cloud_at(nearest, 0.0, _unused, unused2).z * 0.005);
#else
const float spread = 1.0;
#endif
float strength = pow(attenuation_strength_real(difference), spread);
#ifdef EXPERIMENTAL_LOWGLOWNEARCAMERA
vec3 cam_wpos = cam_pos.xyz + focus_pos.xyz + focus_off.xyz;
vec3 cam_diff = light_pos - cam_wpos;
float cam_dist_2 = dot(cam_diff, cam_diff);
// 3 meters away glow returns to the maximum strength.
strength *= clamp(cam_dist_2 / 9.0, 0.25, 1.0);
#endif
vec3 light_color = srgb_to_linear(L.light_col.rgb) * strength;
const float LIGHT_AMBIANCE = 0.025;
color += light_color
* 0.002
// Constant, *should* const fold
* POINT_GLOW_FACTOR;
}
#endif
vec3 apply_point_glow(vec3 wpos, vec3 dir, float max_dist, vec3 color) {
#ifndef POINT_GLOW_FACTOR
return color;
@ -9,44 +52,14 @@ vec3 apply_point_glow(vec3 wpos, vec3 dir, float max_dist, vec3 color) {
// Only access the array once
Light L = lights[i];
vec3 light_pos = L.light_pos.xyz;
// Project light_pos to dir line
float t = max(dot(light_pos - wpos, dir), 0);
vec3 nearest = wpos + dir * min(t, max_dist);
vec3 difference = light_pos - nearest;
float distance_2 = dot(difference, difference);
if (distance_2 > 100000.0) {
continue;
}
#if (CLOUD_MODE >= CLOUD_MODE_HIGH)
vec3 _unused;
float unused2;
float spread = 1.0 / (1.0 + cloud_at(nearest, 0.0, _unused, unused2).z * 0.005);
#else
const float spread = 1.0;
#endif
float strength = pow(attenuation_strength_real(difference), spread);
#ifdef EXPERIMENTAL_LOWGLOWNEARCAMERA
vec3 cam_wpos = cam_pos.xyz + focus_pos.xyz + focus_off.xyz;
vec3 cam_diff = light_pos - cam_wpos;
float cam_dist_2 = dot(cam_diff, cam_diff);
// 3 meters away glow returns to the maximum strength.
strength *= clamp(cam_dist_2 / 9.0, 0.25, 1.0);
#endif
vec3 light_color = srgb_to_linear(L.light_col.rgb) * strength;
const float LIGHT_AMBIANCE = 0.025;
color += light_color
* 0.002
// Constant, *should* const fold
* POINT_GLOW_FACTOR;
apply_point_glow_light(L, wpos, dir, max_dist, color);
}
#endif
float time_since_lightning = tick.x - last_lightning.w;
if (time_since_lightning < MAX_LIGHTNING_PERIOD) {
// Apply lightning
apply_point_glow_light(Light(last_lightning.xyzw + vec4(0, 0, LIGHTNING_HEIGHT, 0), vec4(vec3(0.2, 0.4, 1) * lightning_intensity() * 0.003, 1)), wpos, dir, max_dist, color);
}
return color;
}

View File

@ -20,9 +20,7 @@ uniform u_rain_occlusion {
float rain_occlusion_at(in vec3 fragPos)
{
float bias = -0.2;
vec4 rain_pos = rain_occlusion_texture_mat * vec4(fragPos, 1.0) - vec4(0, 0, bias, 0);
vec4 rain_pos = rain_occlusion_texture_mat * vec4(fragPos, 1.0);
float visibility = textureProj(sampler2DShadow(t_directed_occlusion_maps, s_directed_occlusion_maps), rain_pos);

View File

@ -232,6 +232,34 @@ DirectionalLight get_moon_info(vec4 _dir, float shade_frac/*, vec4 light_pos[2]*
return DirectionalLight(/*dir, */shadow, block/*, get_moon_color(dir), get_moon_brightness(dir)*/);
}
const float LIGHTNING_HEIGHT = 25.0;
const float MAX_LIGHTNING_PERIOD = 5.0;
float lightning_intensity() {
float time_since_lightning = tick.x - last_lightning.w;
return
// Strength
1000000
// Flash
* max(0.0, 1.0 - time_since_lightning * 1.0)
// Reverb
* max(sin(time_of_day.x * 0.4), 0.0);
}
vec3 lightning_at(vec3 wpos) {
float time_since_lightning = tick.x - last_lightning.w;
if (time_since_lightning < MAX_LIGHTNING_PERIOD) {
vec3 diff = wpos + focus_off.xyz - (last_lightning.xyz + vec3(0, 0, LIGHTNING_HEIGHT));
float dist = length(diff);
return vec3(0.5, 0.8, 1.0)
* lightning_intensity()
// Attenuation
/ pow(50.0 + dist, 2);
} else {
return vec3(0.0);
}
}
// // Calculates extra emission and reflectance (due to sunlight / moonlight).
// //
// // reflectence = k_a * i_a + i_a,persistent
@ -437,7 +465,7 @@ float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, ve
light_reflection_factor(norm, dir, normalize(sun_dir - vec3(0.0, 0.1, 0.0)), k_d, k_s, alpha)*/) +
(1.0 - MOON_AMBIANCE) * moon_chroma * moon_shadow * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting) +
emission
);
) + lightning_at(wpos);
/* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE;
diffuse_light =

View File

@ -41,7 +41,6 @@ const int SMOKE = 0;
const int FIRE = 1;
const int GUN_POWDER_SPARK = 2;
const int SHRAPNEL = 3;
const int FIREWORK_BLUE = 4;
const int FIREWORK_GREEN = 5;
const int FIREWORK_PURPLE = 6;
@ -76,6 +75,7 @@ const int DEATH = 34;
const int ENERGY_BUFFING = 35;
const int WEB_STRAND = 36;
const int BLACK_SMOKE = 37;
const int LIGHTNING = 38;
// meters per second squared (acceleration)
const float earth_gravity = 9.807;
@ -602,6 +602,18 @@ void main() {
spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
);
break;
case LIGHTNING:
f_reflect = 0.0;
perp_axis = normalize(cross(inst_dir, vec3(0.0, 0.0, 1.0)));
float z = inst_dir.z * (percent() - 1.0);
vec3 start_off = vec3(abs(fract(vec3(vec2(z) * vec2(0.015, 0.01), 0)) - 0.5) * z * 0.4);
attr = Attr(
inst_dir * percent() + start_off,
vec3(max(3.0, 0.05 * length(start_pos + inst_dir * percent()))),
vec4(10.0, 20.0, 50.0, 1.0),// * (1.0 - length(inst_dir) * 0.1),
identity()//spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
);
break;
default:
attr = Attr(
linear_motion(

View File

@ -244,22 +244,50 @@ void main() {
drop_pos.z *= 0.5 + hash_fast(uvec3(cell2d, 0));
vec3 cell = vec3(cell2d, floor(drop_pos.z * drop_density.z));
if (fract(hash(fract(vec4(cell, 0) * 0.01))) < rain_density * rain_occlusion_at(f_pos.xyz) * 50.0) {
vec3 off = vec3(hash_fast(uvec3(cell * 13)), hash_fast(uvec3(cell * 5)), 0);
vec3 near_cell = (cell + 0.5 + (off - 0.5) * 0.5) / drop_density;
if (rain_occlusion_at(f_pos.xyz + vec3(0, 0, 0.25)) > 0.5) {
#ifdef EXPERIMENTAL_WETNESS
float puddle = clamp((noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.03) - 0.5) * 20.0, 0.0, 1.0) * min(rain_density * 10.0, 1.0);
#else
const float puddle = 1.0;
#endif
float dist = length((drop_pos - near_cell) / vec3(1, 1, 2));
float drop_rad = 0.1;
float distort = max(1.0 - abs(dist - drop_rad) * 100, 0) * 1.5 * max(drop_pos.z - near_cell.z, 0);
k_a += distort;
k_d += distort;
k_s += distort;
f_norm.xy += (drop_pos - near_cell).xy
* max(1.0 - abs(dist - drop_rad) * 30, 0)
* 500.0
* max(drop_pos.z - near_cell.z, 0)
* sign(dist - drop_rad)
* max(drop_pos.z - near_cell.z, 0);
if (fract(hash(fract(vec4(cell, 0) * 0.01))) < rain_density * 2.0 && puddle > 0.3) {
vec3 off = vec3(hash_fast(uvec3(cell * 13)), hash_fast(uvec3(cell * 5)), 0);
vec3 near_cell = (cell + 0.5 + (off - 0.5) * 0.5) / drop_density;
float dist = length((drop_pos - near_cell) / vec3(1, 1, 2));
float drop_rad = 0.1;
float distort = max(1.0 - abs(dist - drop_rad) * 100, 0) * 1.5 * max(drop_pos.z - near_cell.z, 0);
k_a += distort;
k_d += distort;
k_s += distort;
#ifdef EXPERIMENTAL_WETNESS
/* puddle = mix(puddle, 1.0, distort * 10); */
#endif
f_norm.xy += (drop_pos - near_cell).xy
* max(1.0 - abs(dist - drop_rad) * 30, 0)
* 500.0
* max(drop_pos.z - near_cell.z, 0)
* sign(dist - drop_rad)
* max(drop_pos.z - near_cell.z, 0);
}
#ifdef EXPERIMENTAL_WETNESS
if (puddle > 0.0) {
float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+ (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
float hx = (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+ (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
float hy = (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+ (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
f_norm.xy += mix(vec2(0), vec2(h - hx, h - hy) / 0.1 * 0.03, puddle);
alpha = mix(1.0, 0.2, puddle);
f_col.rgb *= mix(1.0, 0.7, puddle);
k_s = mix(k_s, vec3(0.7, 0.7, 1.0), puddle);
}
#endif
}
}
#endif
@ -300,7 +328,7 @@ void main() {
vec3 two_down = f_pos - offset_two;
// Adjust this to change the size of the grid cells relative to the
// number of shadow texels
// number of shadow texels
float grid_cell_to_texel_ratio = 32.0;
vec2 shadowTexSize = textureSize(sampler2D(t_directed_shadow_maps, s_directed_shadow_maps), 0) / grid_cell_to_texel_ratio;
@ -320,7 +348,7 @@ void main() {
return;
}
#endif
float max_light = 0.0;
// After shadows are computed, we use a refracted sun and moon direction.

View File

@ -305,6 +305,7 @@ pub enum ServerChatCommand {
CreateLocation,
DeleteLocation,
WeatherZone,
Lightning,
}
impl ServerChatCommand {
@ -703,6 +704,9 @@ impl ServerChatCommand {
"Create a weather zone",
Some(Admin),
),
ServerChatCommand::Lightning => {
cmd(vec![], "Lightning strike at current position", Some(Admin))
},
}
}
@ -781,6 +785,7 @@ impl ServerChatCommand {
ServerChatCommand::CreateLocation => "create_location",
ServerChatCommand::DeleteLocation => "delete_location",
ServerChatCommand::WeatherZone => "weather_zone",
ServerChatCommand::Lightning => "lightning",
}
}

View File

@ -28,6 +28,9 @@ pub enum Outcome {
is_attack: bool,
reagent: Option<Reagent>, // How can we better define this?
},
Lightning {
pos: Vec3<f32>,
},
ProjectileShot {
pos: Vec3<f32>,
body: comp::Body,
@ -100,6 +103,8 @@ impl Outcome {
pub fn get_pos(&self) -> Option<Vec3<f32>> {
match self {
Outcome::Explosion { pos, .. }
// TODO: Include this, but allow it to be sent to clients when outside of the VD
// | Outcome::Lightning { pos }
| Outcome::ProjectileShot { pos, .. }
| Outcome::ProjectileHit { pos, .. }
| Outcome::Beam { pos, .. }
@ -114,6 +119,7 @@ impl Outcome {
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
Outcome::ExpChange { .. }
| Outcome::ComboChange { .. }
| Outcome::Lightning { .. }
| Outcome::SkillPointGain { .. } => None,
}
}

View File

@ -42,7 +42,7 @@ impl Weather {
// Get the rain velocity for this weather
pub fn rain_vel(&self) -> Vec3<f32> {
const FALL_RATE: f32 = 50.0;
const FALL_RATE: f32 = 30.0;
Vec3::new(self.wind.x, self.wind.y, -FALL_RATE)
}
}

View File

@ -40,6 +40,7 @@ use common::{
link::Is,
mounting::Rider,
npc::{self, get_npc_name},
outcome::Outcome,
parse_cmd_args,
resources::{BattleMode, PlayerPhysicsSettings, Time, TimeOfDay},
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
@ -192,6 +193,7 @@ fn do_command(
ServerChatCommand::CreateLocation => handle_create_location,
ServerChatCommand::DeleteLocation => handle_delete_location,
ServerChatCommand::WeatherZone => handle_weather_zone,
ServerChatCommand::Lightning => handle_lightning,
};
handler(server, client, target, args, cmd)
@ -3666,3 +3668,19 @@ fn handle_weather_zone(
Err(action.help_string())
}
}
fn handle_lightning(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: Vec<String>,
_action: &ServerChatCommand,
) -> CmdResult<()> {
let pos = position(server, client, "player")?.0;
server
.state
.ecs()
.read_resource::<EventBus<Outcome>>()
.emit_now(Outcome::Lightning { pos });
Ok(())
}

View File

@ -1,15 +1,18 @@
use common::{
event::EventBus,
grid::Grid,
outcome::Outcome,
resources::TimeOfDay,
weather::{Weather, WeatherGrid, CELL_SIZE, CHUNKS_PER_CELL},
};
use noise::{NoiseFn, SuperSimplex, Turbulence};
use rand::prelude::*;
use vek::*;
use world::World;
use crate::weather::WEATHER_DT;
fn cell_to_wpos(p: Vec2<i32>) -> Vec2<i32> { p * CELL_SIZE as i32 }
fn cell_to_wpos_center(p: Vec2<i32>) -> Vec2<i32> { p * CELL_SIZE as i32 + CELL_SIZE as i32 / 2 }
#[derive(Clone)]
struct WeatherZone {
@ -19,7 +22,7 @@ struct WeatherZone {
}
struct CellConsts {
rain_factor: f32,
humidity: f32,
}
pub struct WeatherSim {
@ -49,8 +52,9 @@ impl WeatherSim {
}
}
let average_humid = humid_sum / (CHUNKS_PER_CELL * CHUNKS_PER_CELL) as f32;
let rain_factor = (2.0 * average_humid.powf(0.2)).min(1.0);
CellConsts { rain_factor }
CellConsts {
humidity: average_humid.powf(0.2).min(1.0),
}
})
.collect::<Vec<_>>(),
),
@ -82,7 +86,13 @@ impl WeatherSim {
}
// Time step is cell size / maximum wind speed
pub fn tick(&mut self, time_of_day: &TimeOfDay, out: &mut WeatherGrid) {
pub fn tick(
&mut self,
time_of_day: &TimeOfDay,
outcomes: &EventBus<Outcome>,
out: &mut WeatherGrid,
world: &World,
) {
let time = time_of_day.0;
let base_nz = Turbulence::new(
@ -103,7 +113,7 @@ impl WeatherSim {
self.zones[point] = None;
}
} else {
let wpos = cell_to_wpos(point);
let wpos = cell_to_wpos_center(point);
let pos = wpos.as_::<f64>() + time as f64 * 0.1;
@ -111,18 +121,43 @@ impl WeatherSim {
let time_scale = 100_000.0;
let spos = (pos / space_scale).with_z(time as f64 / time_scale);
let pressure =
(base_nz.get(spos.into_array()) * 0.5 + 1.0).clamped(0.0, 1.0) as f32;
let avg_scale = 20_000.0;
let avg_delay = 250_000.0;
let pressure = ((base_nz.get(
(pos / avg_scale)
.with_z(time as f64 / avg_delay)
.into_array(),
) + base_nz.get(
(pos / (avg_scale * 0.25))
.with_z(time as f64 / (avg_delay * 0.25))
.into_array(),
) * 0.5)
* 0.5
+ 1.0)
.clamped(0.0, 1.0) as f32
+ 0.55
- self.consts[point].humidity * 0.6;
const RAIN_CLOUD_THRESHOLD: f32 = 0.26;
cell.cloud = (1.0 - pressure) * 0.5;
cell.rain = (1.0 - pressure - RAIN_CLOUD_THRESHOLD).max(0.0)
* self.consts[point].rain_factor;
const RAIN_CLOUD_THRESHOLD: f32 = 0.25;
cell.cloud = (1.0 - pressure).max(0.0) * 0.5;
cell.rain = ((1.0 - pressure - RAIN_CLOUD_THRESHOLD).max(0.0)
* self.consts[point].humidity
* 2.5)
.powf(0.75);
cell.wind = Vec2::new(
rain_nz.get(spos.into_array()).powi(3) as f32,
rain_nz.get((spos + 1.0).into_array()).powi(3) as f32,
) * 200.0
* (1.0 - pressure);
if cell.rain > 0.2 && cell.cloud > 0.15 && thread_rng().gen_bool(0.01) {
let wpos = wpos.map(|e| {
e as f32 + thread_rng().gen_range(-1.0..1.0) * CELL_SIZE as f32 * 0.5
});
outcomes.emit_now(Outcome::Lightning {
pos: wpos.with_z(world.sim().get_alt_approx(wpos.as_()).unwrap_or(0.0)),
});
}
}
}
}

View File

@ -1,6 +1,8 @@
use common::{resources::TimeOfDay, weather::WeatherGrid};
use common::{event::EventBus, outcome::Outcome, resources::TimeOfDay, weather::WeatherGrid};
use common_ecs::{Origin, Phase, System};
use specs::{Read, Write, WriteExpect};
use specs::{Read, ReadExpect, Write, WriteExpect};
use std::sync::Arc;
use world::World;
use crate::sys::SysScheduler;
@ -15,6 +17,8 @@ impl<'a> System<'a> for Sys {
WriteExpect<'a, WeatherSim>,
WriteExpect<'a, WeatherGrid>,
Write<'a, SysScheduler<Self>>,
ReadExpect<'a, EventBus<Outcome>>,
ReadExpect<'a, Arc<World>>,
);
const NAME: &'static str = "weather::tick";
@ -23,13 +27,13 @@ impl<'a> System<'a> for Sys {
fn run(
_job: &mut common_ecs::Job<Self>,
(game_time, mut sim, mut grid, mut scheduler): Self::SystemData,
(game_time, mut sim, mut grid, mut scheduler, outcomes, world): Self::SystemData,
) {
if scheduler.should_run() {
if grid.size() != sim.size() {
*grid = WeatherGrid::new(sim.size());
}
sim.tick(&game_time, &mut grid);
sim.tick(&game_time, &outcomes, &mut grid, &world);
}
}
}

View File

@ -191,6 +191,7 @@ pub enum SfxEvent {
PoiseChange(PoiseState),
GroundSlam,
Utterance(UtteranceKind, VoiceKind),
Lightning,
}
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
@ -433,6 +434,16 @@ impl SfxMgr {
underwater,
);
},
Outcome::Lightning { pos } => {
let power = (1.0 - pos.distance(audio.listener.pos) / 6_000.0)
.clamped(0.0, 1.0)
.powf(0.75);
if power > 0.0 {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Lightning);
// TODO: Don't use UI sfx, add a way to control position falloff
audio.emit_ui_sfx(sfx_trigger_item, Some(power * 3.0));
}
},
Outcome::GroundSlam { pos, .. } => {
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GroundSlam);
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);

View File

@ -476,6 +476,8 @@ pub enum ExperimentalShader {
/// Display grid lines to visualize the distribution of shadow map texels
/// for the directional light from the sun.
DirectionalShadowMapTexelGrid,
/// Enable rainbows
Rainbows,
/// Disable rainbows
NoRainbows,
/// Make objects appear wet when appropriate.
Wetness,
}

View File

@ -64,6 +64,7 @@ pub struct Globals {
medium: [u32; 4],
select_pos: [i32; 4],
gamma_exposure: [f32; 4],
last_lightning: [f32; 4],
ambiance: f32,
cam_mode: u32,
sprite_render_distance: f32,
@ -108,6 +109,7 @@ impl Globals {
select_pos: Option<Vec3<i32>>,
gamma: f32,
exposure: f32,
last_lightning: (Vec3<f32>, f64),
ambiance: f32,
cam_mode: CameraMode,
sprite_render_distance: f32,
@ -156,6 +158,10 @@ impl Globals {
.unwrap_or_else(Vec4::zero)
.into_array(),
gamma_exposure: [gamma, exposure, 0.0, 0.0],
last_lightning: last_lightning
.0
.with_w(last_lightning.1 as f32)
.into_array(),
ambiance: ambiance.clamped(0.0, 1.0),
cam_mode: cam_mode as u32,
sprite_render_distance,
@ -202,6 +208,7 @@ impl Default for Globals {
None,
1.0,
1.0,
(Vec3::zero(), -1000.0),
1.0,
CameraMode::ThirdPerson,
250.0,

View File

@ -88,6 +88,7 @@ pub enum ParticleMode {
EnergyBuffing = 35,
WebStrand = 36,
BlackSmoke = 37,
Lightning = 38,
}
impl ParticleMode {

View File

@ -195,7 +195,7 @@ impl RainOcclusionPipeline {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Front),
cull_mode: Some(wgpu::Face::Back),
clamp_depth: true,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,

View File

@ -108,6 +108,7 @@ pub struct Scene {
ambient_mgr: AmbientMgr,
integrated_rain_vel: f32,
last_lightning: Option<(Vec3<f32>, f64)>,
}
pub struct SceneData<'a> {
@ -325,6 +326,7 @@ impl Scene {
ambience: ambient::load_ambience_items(),
},
integrated_rain_vel: 0.0,
last_lightning: None,
}
}
@ -426,6 +428,9 @@ impl Scene {
.handle_outcome(outcome, audio, scene_data.client, underwater);
match outcome {
Outcome::Lightning { pos } => {
self.last_lightning = Some((*pos, scene_data.state.get_time()));
},
Outcome::Explosion {
pos,
power,
@ -689,6 +694,7 @@ impl Scene {
self.select_pos.map(|e| e - focus_off.map(|e| e as i32)),
scene_data.gamma,
scene_data.exposure,
self.last_lightning.unwrap_or((Vec3::zero(), -1000.0)),
scene_data.ambiance,
self.camera.get_mode(),
scene_data.sprite_render_distance as f32 - 20.0,

View File

@ -62,6 +62,17 @@ impl ParticleMgr {
let mut rng = rand::thread_rng();
match outcome {
Outcome::Lightning { pos } => {
self.particles.resize_with(self.particles.len() + 800, || {
Particle::new_directed(
Duration::from_secs_f32(rng.gen_range(0.5..1.0)),
time,
ParticleMode::Lightning,
*pos + Vec3::new(0.0, 0.0, rng.gen_range(0.0..600.0)),
*pos,
)
});
},
Outcome::Explosion {
pos,
power,

View File

@ -273,6 +273,7 @@ impl Scene {
None,
scene_data.gamma,
scene_data.exposure,
(Vec3::zero(), -1000.0),
scene_data.ambiance,
self.camera.get_mode(),
250.0,

View File

@ -48,7 +48,7 @@ const SPRITE_LOD_LEVELS: usize = 5;
// For rain occlusion we only need to render the closest chunks.
/// How many chunks are maximally rendered for rain occlusion.
pub const RAIN_OCCLUSION_CHUNKS: usize = 9;
pub const RAIN_OCCLUSION_CHUNKS: usize = 25;
#[derive(Clone, Copy, Debug)]
struct Visibility {