Merge branch 'zesterer/one-way-walls' into 'master'

One-way walls

See merge request veloren/veloren!3943
This commit is contained in:
Joshua Barretto 2023-05-19 17:55:13 +00:00
commit 00b2bcb7b4
9 changed files with 236 additions and 88 deletions

View File

@ -81,6 +81,7 @@ const int BARRELORGAN = 40;
const int POTION_SICKNESS = 41;
const int GIGA_SNOW = 42;
const int CYCLOPS_CHARGE = 43;
const int PORTAL_FIZZ = 45;
// meters per second squared (acceleration)
const float earth_gravity = 9.807;
@ -677,6 +678,19 @@ void main() {
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
);
break;
case PORTAL_FIZZ:
attr = Attr(
inst_dir * (0.7 + pow(percent(), 5)) + vec3(
sin(lifetime * 1.25 + rand0 * 10) + sin(lifetime * 1.3 + rand3 * 10),
sin(lifetime * 1.2 + rand1 * 10) + sin(lifetime * 1.4 + rand4 * 10),
sin(lifetime * 5 + rand2)
) * 0.03,
vec3(pow(1.0 - abs(percent() - 0.5) * 2.0, 0.2)),
vec4(mix(vec3(0.4, 0.2, 0.8), vec3(5, 2, 10), pow(percent(), 2)), 1),
/* vec4(vec3(1.8 - percent() * 2, 0.4 + percent() * 2, 5.0 + rand6), 1), */
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
);
break;
default:
attr = Attr(
linear_motion(

View File

@ -4416,4 +4416,8 @@ GlowIceCrystal: Some((
],
wind_sway: 0.0,
)),
OneWayWall: Some((
variations: [],
wind_sway: 0.0,
)),
}

View File

@ -45,6 +45,8 @@ make_case_elim!(
// 0x12 <= x < 0x20 is reserved for future rocks
Grass = 0x20, // Note: *not* the same as grass sprites
Snow = 0x21,
// Snow to use with sites, to not attract snowfall particles
ArtSnow = 0x22,
// 0x21 <= x < 0x30 is reserved for future grasses
Earth = 0x30,
Sand = 0x31,
@ -58,8 +60,6 @@ make_case_elim!(
// often want to experiment with new kinds of block without allocating them a
// dedicated block kind.
Misc = 0xFE,
// Snow to use with sites, to not attract snowfall particles
ArtSnow = 0xFF,
}
);
@ -368,6 +368,17 @@ impl Block {
.unwrap_or(!matches!(self.kind, BlockKind::Lava))
}
pub fn valid_collision_dir(
&self,
entity_aabb: Aabb<f32>,
block_aabb: Aabb<f32>,
move_dir: Vec3<f32>,
) -> bool {
self.get_sprite().map_or(true, |sprite| {
sprite.valid_collision_dir(entity_aabb, block_aabb, move_dir, self)
})
}
/// Can this block be exploded? If so, what 'power' is required to do so?
/// Note that we don't really define what 'power' is. Consider the units
/// arbitrary and only important when compared to one-another.
@ -387,7 +398,8 @@ impl Block {
SpriteKind::Keyhole
| SpriteKind::KeyDoor
| SpriteKind::BoneKeyhole
| SpriteKind::BoneKeyDoor => None,
| SpriteKind::BoneKeyDoor
| SpriteKind::OneWayWall => None,
SpriteKind::Anvil
| SpriteKind::Cauldron
| SpriteKind::CookingPot

View File

@ -5,6 +5,7 @@ use crate::{
},
lottery::LootSpec,
make_case_elim,
terrain::Block,
};
use hashbrown::HashMap;
use lazy_static::lazy_static;
@ -12,7 +13,7 @@ use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
use strum::EnumIter;
use vek::Vec3;
use vek::*;
make_case_elim!(
sprite_kind,
@ -250,6 +251,7 @@ make_case_elim!(
FireBlock = 0xDF,
IceCrystal = 0xE0,
GlowIceCrystal = 0xE1,
OneWayWall = 0xE2,
}
);
@ -343,7 +345,8 @@ impl SpriteKind {
| SpriteKind::KeyDoor
| SpriteKind::BoneKeyhole
| SpriteKind::BoneKeyDoor
| SpriteKind::Bomb => 1.0,
| SpriteKind::Bomb
| SpriteKind::OneWayWall => 1.0,
// TODO: Figure out if this should be solid or not.
SpriteKind::Shelf => 1.0,
SpriteKind::Lantern => 0.9,
@ -390,6 +393,44 @@ impl SpriteKind {
})
}
pub fn valid_collision_dir(
&self,
entity_aabb: Aabb<f32>,
block_aabb: Aabb<f32>,
move_dir: Vec3<f32>,
parent: &Block,
) -> bool {
match self {
SpriteKind::OneWayWall => {
// Find the intrusion vector of the collision
let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
// Determine an appropriate resolution vector (i.e: the minimum distance
// needed to push out of the block)
let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
let resolve_dir = -dir.map(|e| {
if e.abs().to_bits() == max_axis.to_bits() {
e.signum()
} else {
0.0
}
});
let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
is_moving_into
&& parent.get_ori().map_or(false, |ori| {
Vec2::unit_y()
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
.with_z(0.0)
.map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
.reduce_and()
})
},
_ => true,
}
}
/// What loot table does collecting this sprite draw from?
/// None = block cannot be collected
/// Some(None) = block can be collected, but does not give back an item
@ -661,7 +702,8 @@ impl SpriteKind {
| SpriteKind::BoneKeyhole
| SpriteKind::BoneKeyDoor
| SpriteKind::IceCrystal
| SpriteKind::GlowIceCrystal,
| SpriteKind::GlowIceCrystal
| SpriteKind::OneWayWall,
)
}
}

View File

@ -1389,10 +1389,10 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
fn collision_with<T: BaseVol<Vox = Block> + ReadVol>(
pos: Vec3<f32>,
terrain: &T,
hit: impl Fn(&Block) -> bool,
near_aabb: Aabb<i32>,
radius: f32,
z_range: Range<f32>,
move_dir: Vec3<f32>,
) -> bool {
let player_aabb = player_aabb(pos, radius, z_range);
@ -1402,12 +1402,14 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
let mut collision = false;
// TODO: could short-circuit here
terrain.for_each_in(near_aabb, |block_pos, block| {
if block.is_solid() && hit(&block) {
if block.is_solid() {
let block_aabb = Aabb {
min: block_pos.map(|e| e as f32),
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
};
if player_aabb.collides_with_aabb(block_aabb) {
if player_aabb.collides_with_aabb(block_aabb)
&& block.valid_collision_dir(player_aabb, block_aabb, move_dir)
{
collision = true;
}
}
@ -1416,10 +1418,6 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
collision
}
// Should be easy to just make clippy happy if we want?
#[allow(clippy::trivially_copy_pass_by_ref)]
fn always_hits(_: &Block) -> bool { true }
let (radius, z_min, z_max) = (Vec3::from(cylinder) * scale).into_tuple();
// Probe distances
@ -1459,6 +1457,7 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
const MAX_ATTEMPTS: usize = 16;
pos.0 += pos_delta / increments as f32;
let vel2 = *vel;
let try_colliding_block = |pos: &Pos| {
//prof_span!("most colliding check");
// Calculate the player's AABB
@ -1487,16 +1486,14 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
};
// Determine whether the block's AABB collides with the player's AABB
if block_aabb.collides_with_aabb(player_aabb) {
if player_aabb.collides_with_aabb(block_aabb)
&& block.valid_collision_dir(player_aabb, block_aabb, vel2.0)
{
match &most_colliding {
// Select the minimum of the value from `player_overlap`
Some((_, other_block_aabb, _))
if {
// TODO: comment below is outdated (as of ~1 year ago)
// Find the maximum of the minimum collision axes (this bit
// is weird, trust me that it works)
player_overlap(block_aabb) >= player_overlap(*other_block_aabb)
} => {},
if player_overlap(block_aabb)
>= player_overlap(*other_block_aabb) => {},
_ => most_colliding = Some((block_pos, block_aabb, block)),
}
}
@ -1554,10 +1551,10 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
!collision_with(
Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()),
&terrain,
always_hits,
near_aabb,
radius,
z_range.clone(),
vel.0,
)
}
// ...and there is a collision with a block beneath our current hitbox...
@ -1566,10 +1563,10 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
collision_with(
pos.0 + resolve_dir - Vec3::unit_z() * 1.25,
&terrain,
always_hits,
near_aabb,
radius,
z_range.clone(),
vel.0,
)
} {
// ...block-hop!
@ -1620,10 +1617,10 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
collision_with(
pos.0 - Vec3::unit_z() * 1.1,
&terrain,
always_hits,
near_aabb,
radius,
z_range.clone(),
vel.0,
)
} {
//prof_span!("snap!!");
@ -1694,7 +1691,9 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
};
for dir in 0..4 {
if player_wall_aabbs[dir].collides_with_aabb(block_aabb) {
if player_wall_aabbs[dir].collides_with_aabb(block_aabb)
&& block.valid_collision_dir(player_wall_aabbs[dir], block_aabb, vel.0)
{
wall_dir_collisions[dir] = true;
}
}

View File

@ -96,6 +96,7 @@ pub enum ParticleMode {
GigaSnow = 42,
CyclopsCharge = 43,
SnowStorm = 44,
PortalFizz = 45,
}
impl ParticleMode {

View File

@ -1456,7 +1456,7 @@ impl ParticleMgr {
struct BlockParticles<'a> {
// The function to select the blocks of interest that we should emit from
blocks: fn(&'a BlocksOfInterest) -> &'a [Vec3<i32>],
blocks: fn(&'a BlocksOfInterest) -> BlockParticleSlice<'a>,
// The range, in chunks, that the particles should be generated in from the player
range: usize,
// The emission rate, per block per second, of the generated particles
@ -1469,9 +1469,23 @@ impl ParticleMgr {
cond: fn(&SceneData) -> bool,
}
enum BlockParticleSlice<'a> {
Positions(&'a [Vec3<i32>]),
PositionsAndDirs(&'a [(Vec3<i32>, Vec3<f32>)]),
}
impl<'a> BlockParticleSlice<'a> {
fn len(&self) -> usize {
match self {
Self::Positions(blocks) => blocks.len(),
Self::PositionsAndDirs(blocks) => blocks.len(),
}
}
}
let particles: &[BlockParticles] = &[
BlockParticles {
blocks: |boi| &boi.leaves,
blocks: |boi| BlockParticleSlice::Positions(&boi.leaves),
range: 4,
rate: 0.001,
lifetime: 30.0,
@ -1479,7 +1493,7 @@ impl ParticleMgr {
cond: |_| true,
},
BlockParticles {
blocks: |boi| &boi.drip,
blocks: |boi| BlockParticleSlice::Positions(&boi.drip),
range: 4,
rate: 0.004,
lifetime: 20.0,
@ -1487,7 +1501,7 @@ impl ParticleMgr {
cond: |_| true,
},
BlockParticles {
blocks: |boi| &boi.fires,
blocks: |boi| BlockParticleSlice::Positions(&boi.fires),
range: 2,
rate: 20.0,
lifetime: 0.25,
@ -1495,7 +1509,7 @@ impl ParticleMgr {
cond: |_| true,
},
BlockParticles {
blocks: |boi| &boi.fire_bowls,
blocks: |boi| BlockParticleSlice::Positions(&boi.fire_bowls),
range: 2,
rate: 20.0,
lifetime: 0.25,
@ -1503,7 +1517,7 @@ impl ParticleMgr {
cond: |_| true,
},
BlockParticles {
blocks: |boi| &boi.fireflies,
blocks: |boi| BlockParticleSlice::Positions(&boi.fireflies),
range: 6,
rate: 0.004,
lifetime: 40.0,
@ -1511,7 +1525,7 @@ impl ParticleMgr {
cond: |sd| sd.state.get_day_period().is_dark(),
},
BlockParticles {
blocks: |boi| &boi.flowers,
blocks: |boi| BlockParticleSlice::Positions(&boi.flowers),
range: 5,
rate: 0.002,
lifetime: 40.0,
@ -1519,7 +1533,7 @@ impl ParticleMgr {
cond: |sd| sd.state.get_day_period().is_dark(),
},
BlockParticles {
blocks: |boi| &boi.beehives,
blocks: |boi| BlockParticleSlice::Positions(&boi.beehives),
range: 3,
rate: 0.5,
lifetime: 30.0,
@ -1527,13 +1541,21 @@ impl ParticleMgr {
cond: |sd| sd.state.get_day_period().is_light(),
},
BlockParticles {
blocks: |boi| &boi.snow,
blocks: |boi| BlockParticleSlice::Positions(&boi.snow),
range: 4,
rate: 0.025,
lifetime: 15.0,
mode: ParticleMode::Snow,
cond: |_| true,
},
BlockParticles {
blocks: |boi| BlockParticleSlice::PositionsAndDirs(&boi.one_way_walls),
range: 2,
rate: 12.0,
lifetime: 1.5,
mode: ParticleMode::PortalFizz,
cond: |_| true,
},
];
let ecs = scene_data.state.ecs();
@ -1555,16 +1577,37 @@ impl ParticleMgr {
self.particles
.resize_with(self.particles.len() + particle_count, || {
let block_pos =
Vec3::from(chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32))
+ blocks.choose(&mut rng).copied().unwrap(); // Can't fail
Particle::new(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
block_pos.map(|e: i32| e as f32 + rng.gen::<f32>()),
)
match blocks {
BlockParticleSlice::Positions(blocks) => {
// Can't fail, resize only occurs if blocks > 0
let block_pos = Vec3::from(
chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32),
) + blocks.choose(&mut rng).copied().unwrap();
Particle::new(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
block_pos.map(|e: i32| e as f32 + rng.gen::<f32>()),
)
},
BlockParticleSlice::PositionsAndDirs(blocks) => {
// Can't fail, resize only occurs if blocks > 0
let (block_offset, particle_dir) =
blocks.choose(&mut rng).copied().unwrap();
let block_pos = Vec3::from(
chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32),
) + block_offset;
let particle_pos =
block_pos.map(|e: i32| e as f32 + rng.gen::<f32>());
Particle::new_directed(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
particle_pos,
particle_pos + particle_dir,
)
},
}
})
});
}
@ -1592,19 +1635,39 @@ impl ParticleMgr {
self.particles
.resize_with(self.particles.len() + particle_count, || {
let rel_pos = blocks
.choose(&mut rng)
.copied()
.unwrap()
.map(|e: i32| e as f32 + rng.gen::<f32>()); // Can't fail
let wpos = mat.mul_point(rel_pos);
match blocks {
BlockParticleSlice::Positions(blocks) => {
let rel_pos = blocks
.choose(&mut rng)
.copied()
// Can't fail, resize only occurs if blocks > 0
.unwrap()
.map(|e: i32| e as f32 + rng.gen::<f32>());
let wpos = mat.mul_point(rel_pos);
Particle::new(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
wpos,
)
Particle::new(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
wpos,
)
},
BlockParticleSlice::PositionsAndDirs(blocks) => {
// Can't fail, resize only occurs if blocks > 0
let (block_offset, particle_dir) =
blocks.choose(&mut rng).copied().unwrap();
let particle_pos =
block_offset.map(|e: i32| e as f32 + rng.gen::<f32>());
let wpos = mat.mul_point(particle_pos);
Particle::new_directed(
Duration::from_secs_f32(particles.lifetime),
time,
particles.mode,
wpos,
wpos + mat.mul_direction(particle_dir),
)
},
}
})
}
}

View File

@ -270,41 +270,43 @@ pub fn get_sprite_instances<'a, I: 'a>(
.0; // Awful PRNG
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
let variation = seed as usize % cfg.variations.len();
let key = (sprite, variation);
if !cfg.variations.is_empty() {
let variation = seed as usize % cfg.variations.len();
let key = (sprite, variation);
// NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8
let light = light_map(wpos);
let glow = glow_map(wpos);
// NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8
let light = light_map(wpos);
let glow = glow_map(wpos);
for (lod_level, sprite_data) in lod_levels.iter_mut().zip(&sprite_data[&key]) {
let mat = Mat4::identity()
// Scaling for different LOD resolutions
.scaled_3d(sprite_data.scale)
// Offset
.translated_3d(sprite_data.offset)
.scaled_3d(SPRITE_SCALE)
.rotated_z(f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
rel_pos + Vec3::new(0.5, 0.5, 0.0)
);
// Add an instance for each page in the sprite model
for page in sprite_data.vert_pages.clone() {
// TODO: could be more efficient to create once and clone while
// modifying vert_page
let instance = SpriteInstance::new(
mat,
cfg.wind_sway,
sprite_data.scale.z,
rel_pos.as_(),
ori,
light,
glow,
page,
sprite.is_door(),
);
set_instance(lod_level, instance, wpos);
for (lod_level, sprite_data) in lod_levels.iter_mut().zip(&sprite_data[&key]) {
let mat = Mat4::identity()
// Scaling for different LOD resolutions
.scaled_3d(sprite_data.scale)
// Offset
.translated_3d(sprite_data.offset)
.scaled_3d(SPRITE_SCALE)
.rotated_z(f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
rel_pos + Vec3::new(0.5, 0.5, 0.0)
);
// Add an instance for each page in the sprite model
for page in sprite_data.vert_pages.clone() {
// TODO: could be more efficient to create once and clone while
// modifying vert_page
let instance = SpriteInstance::new(
mat,
cfg.wind_sway,
sprite_data.scale.z,
rel_pos.as_(),
ori,
light,
glow,
page,
sprite.is_door(),
);
set_instance(lod_level, instance, wpos);
}
}
}
}

View File

@ -48,6 +48,7 @@ pub struct BlocksOfInterest {
pub cricket2: Vec<Vec3<i32>>,
pub cricket3: Vec<Vec3<i32>>,
pub frogs: Vec<Vec3<i32>>,
pub one_way_walls: Vec<(Vec3<i32>, Vec3<f32>)>,
// Note: these are only needed for chunks within the iteraction range so this is a potential
// area for optimization
pub interactables: Vec<(Vec3<i32>, Interaction)>,
@ -87,6 +88,7 @@ impl BlocksOfInterest {
let mut cricket2 = Vec::new();
let mut cricket3 = Vec::new();
let mut frogs = Vec::new();
let mut one_way_walls = Vec::new();
let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
@ -172,6 +174,14 @@ impl BlocksOfInterest {
Some(SpriteKind::RepairBench) => {
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
},
Some(SpriteKind::OneWayWall) => one_way_walls.push((
pos,
Vec2::unit_y()
.rotated_z(
std::f32::consts::PI * 0.25 * block.get_ori().unwrap_or(0) as f32,
)
.with_z(0.0),
)),
_ if block.is_mountable() => interactables.push((pos, Interaction::Mount)),
_ => {},
},
@ -217,6 +227,7 @@ impl BlocksOfInterest {
cricket2,
cricket3,
frogs,
one_way_walls,
interactables,
lights,
temperature,