mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/collisions' into 'master'
Collisions Closes #124, #89, and #165 See merge request veloren/veloren!249
This commit is contained in:
commit
44b70dd8a3
@ -1,3 +1,4 @@
|
|||||||
|
#![type_length_limit = "1652471"]
|
||||||
#![feature(
|
#![feature(
|
||||||
euclidean_division,
|
euclidean_division,
|
||||||
duration_float,
|
duration_float,
|
||||||
|
@ -38,7 +38,7 @@ pub struct DeltaTime(pub f32);
|
|||||||
/// too fast, we'd skip important physics events like collisions. This constant determines the
|
/// too fast, we'd skip important physics events like collisions. This constant determines the
|
||||||
/// upper limit. If delta time exceeds this value, the game's physics will begin to produce time
|
/// upper limit. If delta time exceeds this value, the game's physics will begin to produce time
|
||||||
/// lag. Ideally, we'd avoid such a situation.
|
/// lag. Ideally, we'd avoid such a situation.
|
||||||
const MAX_DELTA_TIME: f32 = 0.15;
|
const MAX_DELTA_TIME: f32 = 0.03;
|
||||||
|
|
||||||
pub struct Changes {
|
pub struct Changes {
|
||||||
pub new_chunks: HashSet<Vec2<i32>>,
|
pub new_chunks: HashSet<Vec2<i32>>,
|
||||||
@ -188,6 +188,11 @@ impl State {
|
|||||||
self.ecs.read_resource::<Time>().0
|
self.ecs.read_resource::<Time>().0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current delta time.
|
||||||
|
pub fn get_delta_time(&self) -> f32 {
|
||||||
|
self.ecs.read_resource::<DeltaTime>().0
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to this state's terrain.
|
/// Get a reference to this state's terrain.
|
||||||
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
||||||
self.ecs.read_resource::<TerrainMap>()
|
self.ecs.read_resource::<TerrainMap>()
|
||||||
|
@ -145,18 +145,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Movement
|
// Movement
|
||||||
pos.0 += vel.0 * dt.0;
|
pos.0 += vel.0 * dt.0;
|
||||||
|
|
||||||
// Update OnGround component
|
|
||||||
if terrain
|
|
||||||
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
|
|
||||||
.map(|vox| !vox.is_empty())
|
|
||||||
.unwrap_or(false)
|
|
||||||
&& vel.0.z <= 0.0
|
|
||||||
{
|
|
||||||
on_grounds.insert(entity, OnGround);
|
|
||||||
} else {
|
|
||||||
on_grounds.remove(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integrate forces
|
// Integrate forces
|
||||||
// Friction is assumed to be a constant dependent on location
|
// Friction is assumed to be a constant dependent on location
|
||||||
let friction = 50.0
|
let friction = 50.0
|
||||||
@ -168,16 +156,130 @@ impl<'a> System<'a> for Sys {
|
|||||||
vel.0 = integrate_forces(dt.0, vel.0, friction);
|
vel.0 = integrate_forces(dt.0, vel.0, friction);
|
||||||
|
|
||||||
// Basic collision with terrain
|
// Basic collision with terrain
|
||||||
let mut i = 0.0;
|
let player_rad = 0.3; // half-width of the player's AABB
|
||||||
while terrain
|
let player_height = 1.7;
|
||||||
.get(pos.0.map(|e| e.floor() as i32))
|
|
||||||
.map(|vox| !vox.is_empty())
|
let dist = 2; // distance to probe the terrain for collisions
|
||||||
.unwrap_or(false)
|
let near_iter = (-dist..=dist)
|
||||||
&& i < 6000.0 * dt.0
|
.map(move |i| (-dist..=dist).map(move |j| (-dist..=dist).map(move |k| (i, j, k))))
|
||||||
|
.flatten()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
// Function for determining whether the player at a specific position collides with the ground
|
||||||
|
let collision_with = |pos: Vec3<f32>, near_iter| {
|
||||||
|
for (i, j, k) in near_iter {
|
||||||
|
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k);
|
||||||
|
|
||||||
|
if terrain
|
||||||
|
.get(block_pos)
|
||||||
|
.map(|vox| !vox.is_empty())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let player_aabb = Aabb {
|
||||||
|
min: pos + Vec3::new(-player_rad, -player_rad, 0.0),
|
||||||
|
max: pos + Vec3::new(player_rad, player_rad, player_height),
|
||||||
|
};
|
||||||
|
let block_aabb = Aabb {
|
||||||
|
min: block_pos.map(|e| e as f32),
|
||||||
|
max: block_pos.map(|e| e as f32) + 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if player_aabb.collides_with_aabb(block_aabb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
on_grounds.remove(entity); // Assume we're in the air - unless we can prove otherwise
|
||||||
|
pos.0.z -= 0.0001; // To force collision with the floor
|
||||||
|
|
||||||
|
let mut on_ground = false;
|
||||||
|
let mut attempts = 0; // Don't loop infinitely here
|
||||||
|
|
||||||
|
// While the player is colliding with the terrain...
|
||||||
|
while collision_with(pos.0, near_iter.clone()) && attempts < 32 {
|
||||||
|
// Calculate the player's AABB
|
||||||
|
let player_aabb = Aabb {
|
||||||
|
min: pos.0 + Vec3::new(-player_rad, -player_rad, 0.0),
|
||||||
|
max: pos.0 + Vec3::new(player_rad, player_rad, player_height),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine the block that we are colliding with most (based on minimum collision axis)
|
||||||
|
let (block_pos, block_aabb) = near_iter
|
||||||
|
.clone()
|
||||||
|
// Calculate the block's position in world space
|
||||||
|
.map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k))
|
||||||
|
// Calculate the AABB of the block
|
||||||
|
.map(|block_pos| {
|
||||||
|
(
|
||||||
|
block_pos,
|
||||||
|
Aabb {
|
||||||
|
min: block_pos.map(|e| e as f32),
|
||||||
|
max: block_pos.map(|e| e as f32) + 1.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// Determine whether the block's AABB collides with the player's AABB
|
||||||
|
.filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb))
|
||||||
|
// Make sure the block is actually solid
|
||||||
|
.filter(|(block_pos, _)| {
|
||||||
|
terrain
|
||||||
|
.get(*block_pos)
|
||||||
|
.map(|vox| !vox.is_empty())
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
// Find the maximum of the minimum collision axes (this bit is weird, trust me that it works)
|
||||||
|
.max_by_key(|(_, block_aabb)| {
|
||||||
|
((player_aabb.collision_vector_with_aabb(*block_aabb) / vel.0)
|
||||||
|
.map(|e| e.abs())
|
||||||
|
.reduce_partial_min()
|
||||||
|
* 1000.0) as i32
|
||||||
|
})
|
||||||
|
.expect("Collision detected, but no colliding blocks found!");
|
||||||
|
|
||||||
|
// Find the intrusion vector of the collision
|
||||||
|
let dir = player_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() == max_axis { e } else { 0.0 });
|
||||||
|
|
||||||
|
// When the resolution direction is pointing upwards, we must be on the ground
|
||||||
|
if resolve_dir.z > 0.0 {
|
||||||
|
on_ground = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the resolution direction is non-vertical, we must be colliding with a wall
|
||||||
|
// If the space above is free...
|
||||||
|
if resolve_dir.z == 0.0
|
||||||
|
&& !collision_with(pos.0 + Vec3::unit_z() * 1.1, near_iter.clone())
|
||||||
|
{
|
||||||
|
// ...block-hop!
|
||||||
|
pos.0.z = (pos.0.z + 1.0).ceil();
|
||||||
|
on_ground = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Resolve the collision normally
|
||||||
|
pos.0 += resolve_dir;
|
||||||
|
vel.0 = vel
|
||||||
|
.0
|
||||||
|
.map2(resolve_dir, |e, d| if d == 0.0 { e } else { 0.0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if on_ground {
|
||||||
|
on_grounds.insert(entity, OnGround);
|
||||||
|
// If we're not on the ground but the space below us is free, then "snap" to the ground
|
||||||
|
} else if collision_with(pos.0 - Vec3::unit_z() * 1.0, near_iter.clone())
|
||||||
|
&& vel.0.z < 0.0
|
||||||
|
&& vel.0.z > -1.0
|
||||||
{
|
{
|
||||||
pos.0.z += 0.0025;
|
pos.0.z = (pos.0.z - 0.05).floor();
|
||||||
vel.0.z = 0.0;
|
on_grounds.insert(entity, OnGround);
|
||||||
i += 1.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ impl Server {
|
|||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 280.0)));
|
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 305.0)));
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
state,
|
state,
|
||||||
|
@ -31,7 +31,7 @@ impl Bone {
|
|||||||
/// Change the current bone to be more like `target`.
|
/// Change the current bone to be more like `target`.
|
||||||
fn interpolate(&mut self, target: &Bone, dt: f32) {
|
fn interpolate(&mut self, target: &Bone, dt: f32) {
|
||||||
// TODO: Make configurable.
|
// TODO: Make configurable.
|
||||||
let factor = dbg!((15.0 * dt).min(1.0));
|
let factor = (15.0 * dt).min(1.0);
|
||||||
self.offset += (target.offset - self.offset) * factor;
|
self.offset += (target.offset - self.offset) * factor;
|
||||||
self.ori = vek::ops::Slerp::slerp(self.ori, target.ori, factor);
|
self.ori = vek::ops::Slerp::slerp(self.ori, target.ori, factor);
|
||||||
self.scale += (target.scale - self.scale) * factor;
|
self.scale += (target.scale - self.scale) * factor;
|
||||||
|
@ -118,6 +118,7 @@ impl Scene {
|
|||||||
Vec3::zero(),
|
Vec3::zero(),
|
||||||
-Vec3::unit_y(),
|
-Vec3::unit_y(),
|
||||||
Rgba::broadcast(1.0),
|
Rgba::broadcast(1.0),
|
||||||
|
1.0 / 60.0, // TODO: Use actual deltatime here?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use client::Client;
|
use client::Client;
|
||||||
use common::vol::ReadVol;
|
use common::vol::{ReadVol, Vox};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
const NEAR_PLANE: f32 = 0.1;
|
const NEAR_PLANE: f32 = 0.01;
|
||||||
const FAR_PLANE: f32 = 10000.0;
|
const FAR_PLANE: f32 = 10000.0;
|
||||||
|
|
||||||
const INTERP_TIME: f32 = 0.05;
|
const INTERP_TIME: f32 = 0.1;
|
||||||
|
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
tgt_focus: Vec3<f32>,
|
tgt_focus: Vec3<f32>,
|
||||||
@ -40,13 +40,13 @@ impl Camera {
|
|||||||
pub fn compute_dependents(&self, client: &Client) -> (Mat4<f32>, Mat4<f32>, Vec3<f32>) {
|
pub fn compute_dependents(&self, client: &Client) -> (Mat4<f32>, Mat4<f32>, Vec3<f32>) {
|
||||||
let dist = {
|
let dist = {
|
||||||
let (start, end) = (
|
let (start, end) = (
|
||||||
self.focus,
|
|
||||||
self.focus
|
self.focus
|
||||||
+ (Vec3::new(
|
+ (Vec3::new(
|
||||||
-f32::sin(self.ori.x) * f32::cos(self.ori.y),
|
-f32::sin(self.ori.x) * f32::cos(self.ori.y),
|
||||||
-f32::cos(self.ori.x) * f32::cos(self.ori.y),
|
-f32::cos(self.ori.x) * f32::cos(self.ori.y),
|
||||||
f32::sin(self.ori.y),
|
f32::sin(self.ori.y),
|
||||||
) * self.dist),
|
) * self.dist),
|
||||||
|
self.focus,
|
||||||
);
|
);
|
||||||
|
|
||||||
match client
|
match client
|
||||||
@ -55,9 +55,10 @@ impl Camera {
|
|||||||
.ray(start, end)
|
.ray(start, end)
|
||||||
.ignore_error()
|
.ignore_error()
|
||||||
.max_iter(500)
|
.max_iter(500)
|
||||||
|
.until(|b| b.is_empty())
|
||||||
.cast()
|
.cast()
|
||||||
{
|
{
|
||||||
(d, Ok(Some(_))) => f32::min(d - 1.0, self.dist),
|
(d, Ok(Some(_))) => f32::min(self.dist - d - 0.03, self.dist),
|
||||||
(_, Ok(None)) => self.dist,
|
(_, Ok(None)) => self.dist,
|
||||||
(_, Err(_)) => self.dist,
|
(_, Err(_)) => self.dist,
|
||||||
}
|
}
|
||||||
|
@ -474,10 +474,9 @@ impl FigureMgr {
|
|||||||
let time = client.state().get_time();
|
let time = client.state().get_time();
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
let view_distance = client.view_distance().unwrap_or(1);
|
let view_distance = client.view_distance().unwrap_or(1);
|
||||||
|
let dt = client.state().get_delta_time();
|
||||||
// Get player position.
|
// Get player position.
|
||||||
let player_pos = client
|
let player_pos = ecs
|
||||||
.state()
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<comp::Pos>()
|
.read_storage::<comp::Pos>()
|
||||||
.get(client.entity())
|
.get(client.entity())
|
||||||
.map_or(Vec3::zero(), |pos| pos.0);
|
.map_or(Vec3::zero(), |pos| pos.0);
|
||||||
@ -580,11 +579,8 @@ impl FigureMgr {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
state.skeleton.interpolate(
|
state.skeleton.interpolate(&target_skeleton, dt);
|
||||||
&target_skeleton,
|
state.update(renderer, pos.0, ori.0, col, dt);
|
||||||
client.state().ecs().read_resource::<DeltaTime>().0,
|
|
||||||
);
|
|
||||||
state.update(renderer, pos.0, ori.0, col);
|
|
||||||
}
|
}
|
||||||
Body::Quadruped(_) => {
|
Body::Quadruped(_) => {
|
||||||
let state = self.quadruped_states.entry(entity).or_insert_with(|| {
|
let state = self.quadruped_states.entry(entity).or_insert_with(|| {
|
||||||
@ -612,11 +608,8 @@ impl FigureMgr {
|
|||||||
_ => state.skeleton_mut().clone(),
|
_ => state.skeleton_mut().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
state.skeleton.interpolate(
|
state.skeleton.interpolate(&target_skeleton, dt);
|
||||||
&target_skeleton,
|
state.update(renderer, pos.0, ori.0, col, dt);
|
||||||
client.state().ecs().read_resource::<DeltaTime>().0,
|
|
||||||
);
|
|
||||||
state.update(renderer, pos.0, ori.0, col);
|
|
||||||
}
|
}
|
||||||
Body::QuadrupedMedium(_) => {
|
Body::QuadrupedMedium(_) => {
|
||||||
let state =
|
let state =
|
||||||
@ -651,11 +644,8 @@ impl FigureMgr {
|
|||||||
_ => state.skeleton_mut().clone(),
|
_ => state.skeleton_mut().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
state.skeleton.interpolate(
|
state.skeleton.interpolate(&target_skeleton, dt);
|
||||||
&target_skeleton,
|
state.update(renderer, pos.0, ori.0, col, dt);
|
||||||
client.state().ecs().read_resource::<DeltaTime>().0,
|
|
||||||
);
|
|
||||||
state.update(renderer, pos.0, ori.0, col);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: Non-character actors
|
// TODO: Non-character actors
|
||||||
@ -741,6 +731,8 @@ pub struct FigureState<S: Skeleton> {
|
|||||||
bone_consts: Consts<FigureBoneData>,
|
bone_consts: Consts<FigureBoneData>,
|
||||||
locals: Consts<FigureLocals>,
|
locals: Consts<FigureLocals>,
|
||||||
skeleton: S,
|
skeleton: S,
|
||||||
|
pos: Vec3<f32>,
|
||||||
|
ori: Vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Skeleton> FigureState<S> {
|
impl<S: Skeleton> FigureState<S> {
|
||||||
@ -751,6 +743,8 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
|
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
|
||||||
skeleton,
|
skeleton,
|
||||||
|
pos: Vec3::zero(),
|
||||||
|
ori: Vec3::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,9 +754,14 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
ori: Vec3<f32>,
|
ori: Vec3<f32>,
|
||||||
col: Rgba<f32>,
|
col: Rgba<f32>,
|
||||||
|
dt: f32,
|
||||||
) {
|
) {
|
||||||
|
// Update interpolate pos
|
||||||
|
self.pos = Lerp::lerp(self.pos, pos, (0.3f32).powf(1.0 / 60.0).powf(1.0 / dt));
|
||||||
|
self.ori = Slerp::slerp(self.ori, ori, (0.15f32).powf(1.0 / 60.0).powf(1.0 / dt));
|
||||||
|
|
||||||
let mat = Mat4::<f32>::identity()
|
let mat = Mat4::<f32>::identity()
|
||||||
* Mat4::translation_3d(pos)
|
* Mat4::translation_3d(self.pos)
|
||||||
* Mat4::rotation_z(-ori.x.atan2(ori.y))
|
* Mat4::rotation_z(-ori.x.atan2(ori.y))
|
||||||
* Mat4::scaling_3d(Vec3::from(0.8));
|
* Mat4::scaling_3d(Vec3::from(0.8));
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ impl Scene {
|
|||||||
let tilt = self.camera.get_orientation().y;
|
let tilt = self.camera.get_orientation().y;
|
||||||
let dist = self.camera.get_distance();
|
let dist = self.camera.get_distance();
|
||||||
self.camera
|
self.camera
|
||||||
.set_focus_pos(player_pos + Vec3::unit_z() * (2.1 - tilt.min(0.0) * dist * 0.75));
|
.set_focus_pos(player_pos + Vec3::unit_z() * (1.95 - tilt.min(0.0) * dist * 0.75));
|
||||||
|
|
||||||
// Tick camera for interpolation.
|
// Tick camera for interpolation.
|
||||||
self.camera.update(client.state().get_time());
|
self.camera.update(client.state().get_time());
|
||||||
|
@ -151,7 +151,7 @@ impl PlayState for SessionState {
|
|||||||
self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
||||||
|
|
||||||
// Perform an in-game tick.
|
// Perform an in-game tick.
|
||||||
if let Err(err) = self.tick(clock.get_last_delta()) {
|
if let Err(err) = self.tick(clock.get_avg_delta()) {
|
||||||
error!("Failed to tick the scene: {:?}", err);
|
error!("Failed to tick the scene: {:?}", err);
|
||||||
return PlayStateResult::Pop;
|
return PlayStateResult::Pop;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user