mirror of
https://gitlab.com/veloren/veloren.git
synced 2025-07-25 21:02:31 +00:00
Merge branch 'isse/splish-splosh' into 'master'
Water splashes See merge request veloren/veloren!4640
This commit is contained in:
@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- IP Bans
|
||||
- Npcs can catch you stealing.
|
||||
- Button to show unknown recipes.
|
||||
- Water splashes and river particles.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -141,6 +141,27 @@
|
||||
threshold: 7.0,
|
||||
subtitle: "subtitle-lavapool",
|
||||
),
|
||||
SplashSmall: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.footsteps.splash_small",
|
||||
],
|
||||
threshold: 0.0,
|
||||
subtitle: "subtitle-splash"
|
||||
),
|
||||
SplashMedium: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.footsteps.splash_medium",
|
||||
],
|
||||
threshold: 0.0,
|
||||
subtitle: "subtitle-splash"
|
||||
),
|
||||
SplashBig: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.footsteps.splash_big",
|
||||
],
|
||||
threshold: 0.0,
|
||||
subtitle: "subtitle-splash"
|
||||
),
|
||||
//
|
||||
// Character States
|
||||
//
|
||||
|
BIN
assets/voxygen/audio/sfx/footsteps/splash_big.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/footsteps/splash_big.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/footsteps/splash_medium.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/footsteps/splash_medium.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/footsteps/splash_small.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/footsteps/splash_small.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -23,6 +23,7 @@ subtitle-swim = Swimming
|
||||
subtitle-climb = Climbing
|
||||
subtitle-damage = Damage
|
||||
subtitle-death = Death
|
||||
subtitle-splash = Splash
|
||||
|
||||
subtitle-wield_bow = Bow equipped
|
||||
subtitle-unwield_bow = Bow unequipped
|
||||
|
@ -24,6 +24,7 @@ layout(location = 0) in vec3 f_pos;
|
||||
layout(location = 1) flat in vec3 f_norm;
|
||||
layout(location = 2) in vec4 f_col;
|
||||
layout(location = 3) in float f_reflect;
|
||||
layout(location = 4) flat in int f_mode;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
layout(location = 1) out uvec4 tgt_mat;
|
||||
@ -79,8 +80,10 @@ void main() {
|
||||
// CPU) we need to some how find an approximation of how much the sun is blocked. We do this by fading out the sun
|
||||
// as the particle moves underground. This isn't perfect, but it does at least mean that particles don't look like
|
||||
// they're exposed to the sun when in dungeons
|
||||
const float SUN_FADEOUT_DIST = 20.0;
|
||||
sun_info.block *= clamp((f_pos.z - f_alt) / SUN_FADEOUT_DIST + 1, 0, 1);
|
||||
const float LIGHT_FADEOUT_OFFSET = 50.0;
|
||||
const float LIGHT_FADEOUT_DIST = 20.0;
|
||||
sun_info.block *= clamp((f_pos.z - f_alt + LIGHT_FADEOUT_OFFSET) / LIGHT_FADEOUT_DIST + 1, 0, 1);
|
||||
moon_info.block *= clamp((f_pos.z - f_alt + LIGHT_FADEOUT_OFFSET) / LIGHT_FADEOUT_DIST + 1, 0, 1);
|
||||
|
||||
// To account for prior saturation.
|
||||
float max_light = 0.0;
|
||||
@ -110,5 +113,14 @@ void main() {
|
||||
|
||||
// Temporarily disable particle transparency to avoid artifacts
|
||||
tgt_color = vec4(surf_color, 1.0 /*f_col.a*/);
|
||||
tgt_mat = uvec4(uvec3((f_norm + 1.0) * 127.0), MAT_BLOCK);
|
||||
|
||||
uint material = MAT_BLOCK;
|
||||
|
||||
const int WATER_FOAM = 64;
|
||||
|
||||
if (f_mode == WATER_FOAM) {
|
||||
material = MAT_FLUID;
|
||||
}
|
||||
|
||||
tgt_mat = uvec4(uvec3((f_norm + 1.0) * 127.0), material);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ layout(location = 2) out vec4 f_col;
|
||||
//layout(location = x) out float f_ao;
|
||||
//layout(location = x) out float f_light;
|
||||
layout(location = 3) out float f_reflect;
|
||||
layout(location = 4) flat out int f_mode;
|
||||
|
||||
const float SCALE = 1.0 / 11.0;
|
||||
|
||||
@ -100,6 +101,7 @@ const int SPORE = 60;
|
||||
const int SURPRISE_EGG = 61;
|
||||
const int FLAME_TORNADO = 62;
|
||||
const int POISON = 63;
|
||||
const int WATER_FOAM = 64;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -1058,6 +1060,15 @@ void main() {
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case WATER_FOAM:
|
||||
f_reflect = 0.1;
|
||||
attr = Attr(
|
||||
inst_dir * pow(percent(), 0.5) * 0.5 + percent() * percent() * vec3(0, 0, -50),
|
||||
vec3((1.5 * (1 - slow_start(0.2)))),
|
||||
vec4(1.0, 1.0, 1.0, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
@ -1097,6 +1108,8 @@ void main() {
|
||||
//vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0;
|
||||
f_col = vec4(attr.col.rgb, attr.col.a);
|
||||
|
||||
f_mode = inst_mode;
|
||||
|
||||
gl_Position =
|
||||
all_mat *
|
||||
vec4(f_pos, 1);
|
||||
|
@ -51,7 +51,9 @@ impl Component for PosVelOriDefer {
|
||||
/// no need to send it via network
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq)]
|
||||
pub struct PreviousPhysCache {
|
||||
pub velocity: Vec3<f32>,
|
||||
pub velocity_dt: Vec3<f32>,
|
||||
pub in_fluid: Option<Fluid>,
|
||||
/// Center of bounding sphere that encompasses the entity along its path for
|
||||
/// this tick
|
||||
pub center: Vec3<f32>,
|
||||
|
@ -186,6 +186,12 @@ pub enum Outcome {
|
||||
uid: Uid,
|
||||
head: usize,
|
||||
},
|
||||
Splash {
|
||||
vel: Vec3<f32>,
|
||||
pos: Vec3<f32>,
|
||||
mass: f32,
|
||||
kind: comp::fluid_dynamics::LiquidKind,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -225,7 +231,8 @@ impl Outcome {
|
||||
| Outcome::TeleportedByPortal { pos}
|
||||
| Outcome::FromTheAshes { pos }
|
||||
| Outcome::ClayGolemDash { pos }
|
||||
| Outcome::Glider { pos, .. } => Some(*pos),
|
||||
| Outcome::Glider { pos, .. }
|
||||
| Outcome::Splash { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. }
|
||||
| Outcome::DamagedBlock { pos, .. }
|
||||
| Outcome::SpriteUnlocked { pos }
|
||||
|
@ -10,6 +10,7 @@ mod interpolation;
|
||||
pub mod melee;
|
||||
mod mount;
|
||||
pub mod phys;
|
||||
mod phys_events;
|
||||
pub mod projectile;
|
||||
mod shockwave;
|
||||
mod stats;
|
||||
@ -34,6 +35,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
&mount::Sys::sys_name(),
|
||||
&stats::Sys::sys_name(),
|
||||
]);
|
||||
dispatch::<phys_events::Sys>(dispatch_builder, &[&phys::Sys::sys_name()]);
|
||||
dispatch::<projectile::Sys>(dispatch_builder, &[&phys::Sys::sys_name()]);
|
||||
dispatch::<shockwave::Sys>(dispatch_builder, &[&phys::Sys::sys_name()]);
|
||||
dispatch::<beam::Sys>(dispatch_builder, &[&phys::Sys::sys_name()]);
|
||||
|
@ -365,7 +365,9 @@ impl<'a> PhysicsData<'a> {
|
||||
.write
|
||||
.previous_phys_cache
|
||||
.insert(entity, PreviousPhysCache {
|
||||
velocity: Vec3::zero(),
|
||||
velocity_dt: Vec3::zero(),
|
||||
in_fluid: None,
|
||||
center: Vec3::zero(),
|
||||
collision_boundary: 0.0,
|
||||
scale: 0.0,
|
||||
@ -378,11 +380,12 @@ impl<'a> PhysicsData<'a> {
|
||||
}
|
||||
|
||||
// Update PreviousPhysCache
|
||||
for (_, vel, position, ori, phys_cache, collider, scale, cs) in (
|
||||
for (_, vel, position, ori, phys_state, phys_cache, collider, scale, cs) in (
|
||||
&self.read.entities,
|
||||
&self.write.velocities,
|
||||
&self.write.positions,
|
||||
&self.write.orientations,
|
||||
&self.write.physics_states,
|
||||
&mut self.write.previous_phys_cache,
|
||||
&self.read.colliders,
|
||||
self.read.scales.maybe(),
|
||||
@ -397,6 +400,8 @@ impl<'a> PhysicsData<'a> {
|
||||
let half_height = (z_max - z_min) / 2.0;
|
||||
|
||||
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
||||
phys_cache.velocity = vel.0;
|
||||
phys_cache.in_fluid = phys_state.in_fluid;
|
||||
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
||||
let flat_radius = collider.bounding_radius() * scale;
|
||||
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
||||
|
57
common/systems/src/phys_events.rs
Normal file
57
common/systems/src/phys_events.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use common::{
|
||||
comp::{Fluid, Mass, PhysicsState, Pos, PreviousPhysCache, Vel},
|
||||
event::EventBus,
|
||||
outcome::Outcome,
|
||||
};
|
||||
use common_ecs::System;
|
||||
use specs::{Join, Read, ReadStorage};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, EventBus<Outcome>>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadStorage<'a, PreviousPhysCache>,
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Mass>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = "phys_events";
|
||||
const ORIGIN: common_ecs::Origin = common_ecs::Origin::Common;
|
||||
const PHASE: common_ecs::Phase = common_ecs::Phase::Create;
|
||||
|
||||
fn run(
|
||||
_job: &mut common_ecs::Job<Self>,
|
||||
(outcomes, physics_states, previous_phys_cache, velocities, positions, masses): Self::SystemData,
|
||||
) {
|
||||
let mut outcomes = outcomes.emitter();
|
||||
for (physics_state, prev, vel, pos, mass) in (
|
||||
&physics_states,
|
||||
&previous_phys_cache,
|
||||
&velocities,
|
||||
&positions,
|
||||
&masses,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Only splash when going from air into a liquid
|
||||
if let (Some(Fluid::Liquid { kind, .. }), Some(Fluid::Air { .. })) =
|
||||
(physics_state.in_fluid, prev.in_fluid)
|
||||
{
|
||||
outcomes.emit(Outcome::Splash {
|
||||
pos: pos.0,
|
||||
vel: if vel.0.magnitude_squared() > prev.velocity.magnitude_squared() {
|
||||
vel.0
|
||||
} else {
|
||||
prev.velocity
|
||||
},
|
||||
mass: mass.0,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -130,6 +130,9 @@ pub enum SfxEvent {
|
||||
Lavapool,
|
||||
Idle,
|
||||
Swim,
|
||||
SplashSmall,
|
||||
SplashMedium,
|
||||
SplashBig,
|
||||
Run(BlockKind),
|
||||
QuadRun(BlockKind),
|
||||
Roll,
|
||||
@ -897,6 +900,22 @@ impl SfxMgr {
|
||||
error!("Couldn't get position of entity that lost head");
|
||||
}
|
||||
},
|
||||
Outcome::Splash { vel, pos, mass, .. } => {
|
||||
let magnitude = (-vel.z).max(0.0);
|
||||
let energy = mass * magnitude;
|
||||
|
||||
if energy > 0.0 {
|
||||
let (sfx, volume) = if energy < 10.0 {
|
||||
(SfxEvent::SplashSmall, energy / 20.0)
|
||||
} else if energy < 100.0 {
|
||||
(SfxEvent::SplashMedium, (energy - 10.0) / 90.0 + 0.5)
|
||||
} else {
|
||||
(SfxEvent::SplashBig, (energy / 100.0).sqrt() + 0.5)
|
||||
};
|
||||
let sfx_trigger_item = triggers.get_key_value(&sfx);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(volume.max(10.0)));
|
||||
}
|
||||
},
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => {},
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ pub enum ParticleMode {
|
||||
SurpriseEgg = 61,
|
||||
FlameTornado = 62,
|
||||
Poison = 63,
|
||||
WaterFoam = 64,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -857,7 +857,7 @@ where
|
||||
);
|
||||
instances
|
||||
},
|
||||
blocks_of_interest: BlocksOfInterest::from_blocks(block_iter, 0.0, 10.0, 0.0, dyna),
|
||||
blocks_of_interest: BlocksOfInterest::from_blocks(block_iter, Vec3::zero(), 10.0, 0.0, dyna),
|
||||
blocks_offset: *offset,
|
||||
}));
|
||||
});
|
||||
|
@ -506,6 +506,51 @@ impl ParticleMgr {
|
||||
}
|
||||
};
|
||||
},
|
||||
Outcome::Splash {
|
||||
vel,
|
||||
pos,
|
||||
mass,
|
||||
kind,
|
||||
} => {
|
||||
let mode = match kind {
|
||||
comp::fluid_dynamics::LiquidKind::Water => ParticleMode::WaterFoam,
|
||||
comp::fluid_dynamics::LiquidKind::Lava => ParticleMode::CampfireFire,
|
||||
};
|
||||
let magnitude = (-vel.z).max(0.0);
|
||||
let energy = mass * magnitude;
|
||||
if energy > 0.0 {
|
||||
let count = ((0.6 * energy.sqrt()).ceil() as usize).min(500);
|
||||
let mut i = 0;
|
||||
let r = 0.5 / count as f32;
|
||||
self.particles
|
||||
.resize_with(self.particles.len() + count, || {
|
||||
let t = i as f32 / count as f32 + rng.gen_range(-r..=r);
|
||||
i += 1;
|
||||
let angle = t * TAU;
|
||||
let s = angle.sin();
|
||||
let c = angle.cos();
|
||||
let energy = energy
|
||||
* f32::abs(rng.gen_range(0.0..1.0) + rng.gen_range(0.0..1.0) - 0.5);
|
||||
|
||||
let axis = -Vec3::unit_z();
|
||||
let plane = Vec3::new(c, s, 0.0);
|
||||
|
||||
let pos = *pos + plane * rng.gen_range(0.0..0.5);
|
||||
|
||||
let energy = energy.sqrt() * 0.5;
|
||||
|
||||
let dir = plane * (1.0 + energy) - axis * energy;
|
||||
|
||||
Particle::new_directed(
|
||||
Duration::from_millis(4000),
|
||||
time,
|
||||
mode,
|
||||
pos,
|
||||
pos + dir,
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::ProjectileShot { .. }
|
||||
| Outcome::Beam { .. }
|
||||
| Outcome::ExpChange { .. }
|
||||
@ -2447,6 +2492,14 @@ impl ParticleMgr {
|
||||
mode: ParticleMode::Spore,
|
||||
cond: |_| true,
|
||||
},
|
||||
BlockParticles {
|
||||
blocks: |boi| BlockParticleSlice::PositionsAndDirs(&boi.waterfall),
|
||||
range: 2,
|
||||
rate: 4.0,
|
||||
lifetime: 5.0,
|
||||
mode: ParticleMode::WaterFoam,
|
||||
cond: |_| true,
|
||||
},
|
||||
];
|
||||
|
||||
let ecs = scene_data.state.ecs();
|
||||
|
@ -255,7 +255,7 @@ fn mesh_worker(
|
||||
span!(_guard, "mesh_worker");
|
||||
let blocks_of_interest = BlocksOfInterest::from_blocks(
|
||||
chunk.iter_changed().map(|(pos, block)| (pos, *block)),
|
||||
chunk.meta().river_velocity().magnitude_squared(),
|
||||
chunk.meta().river_velocity(),
|
||||
chunk.meta().temp(),
|
||||
chunk.meta().humidity(),
|
||||
&*chunk,
|
||||
|
@ -40,6 +40,7 @@ pub struct BlocksOfInterest {
|
||||
pub grass: Vec<Vec3<i32>>,
|
||||
pub slow_river: Vec<Vec3<i32>>,
|
||||
pub fast_river: Vec<Vec3<i32>>,
|
||||
pub waterfall: Vec<(Vec3<i32>, Vec3<f32>)>,
|
||||
pub lavapool: Vec<Vec3<i32>>,
|
||||
pub fires: Vec<Vec3<i32>>,
|
||||
pub smokers: Vec<SmokerProperties>,
|
||||
@ -68,7 +69,7 @@ pub struct BlocksOfInterest {
|
||||
impl BlocksOfInterest {
|
||||
pub fn from_blocks(
|
||||
blocks: impl Iterator<Item = (Vec3<i32>, Block)>,
|
||||
river_speed_sq: f32,
|
||||
river_velocity: Vec3<f32>,
|
||||
temperature: f32,
|
||||
humidity: f32,
|
||||
chunk: &impl ReadVol<Vox = Block>,
|
||||
@ -79,6 +80,7 @@ impl BlocksOfInterest {
|
||||
let mut grass = Vec::new();
|
||||
let mut slow_river = Vec::new();
|
||||
let mut fast_river = Vec::new();
|
||||
let mut waterfall = Vec::new();
|
||||
let mut lavapool = Vec::new();
|
||||
let mut fires = Vec::new();
|
||||
let mut smokers = Vec::new();
|
||||
@ -124,9 +126,38 @@ impl BlocksOfInterest {
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
// Assign a river speed to water blocks depending on river velocity
|
||||
BlockKind::Water if river_speed_sq > 0.9_f32.powi(2) => fast_river.push(pos),
|
||||
BlockKind::Water if river_speed_sq > 0.3_f32.powi(2) => slow_river.push(pos),
|
||||
BlockKind::Water => {
|
||||
let is_waterfall = chunk
|
||||
.get(pos + vek::Vec3::unit_z())
|
||||
.is_ok_and(|b| b.is_air())
|
||||
&& [
|
||||
vek::Vec2::new(0, 1),
|
||||
vek::Vec2::new(1, 0),
|
||||
vek::Vec2::new(0, -1),
|
||||
vek::Vec2::new(-1, 0),
|
||||
]
|
||||
.iter()
|
||||
.map(|p| {
|
||||
(1..=2)
|
||||
.take_while(|i| {
|
||||
chunk.get(pos + p.with_z(*i)).is_ok_and(|b| b.is_liquid())
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.any(|s| s >= 2);
|
||||
|
||||
if is_waterfall {
|
||||
waterfall.push((pos, river_velocity));
|
||||
}
|
||||
|
||||
let river_speed_sq = river_velocity.magnitude_squared();
|
||||
// Assign a river speed to water blocks depending on river velocity
|
||||
if is_waterfall || river_speed_sq > 0.9_f32.powi(2) {
|
||||
fast_river.push(pos)
|
||||
} else if river_speed_sq > 0.3_f32.powi(2) {
|
||||
slow_river.push(pos)
|
||||
}
|
||||
},
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
BlockKind::Lava
|
||||
if chunk
|
||||
@ -267,6 +298,7 @@ impl BlocksOfInterest {
|
||||
grass,
|
||||
slow_river,
|
||||
fast_river,
|
||||
waterfall,
|
||||
lavapool,
|
||||
fires,
|
||||
smokers,
|
||||
|
Reference in New Issue
Block a user