Address Imbris's MR 1888 comments, and add changelog entry.

- Use Ori::{new,to_quat} and make the field private.
- Update/capitalize/add various comments.
- Implicitly drop scope guards where applicable.
- Take !Copy colliders by reference instead of cloning.
- s/cylinder_voxel_collision/box_voxel_collision/
- Unindent some physics code with a continue.
This commit is contained in:
Avi Weinstock 2021-03-14 22:36:59 -04:00
parent 1e9ee3d243
commit 9304ecab3d
10 changed files with 104 additions and 104 deletions

View File

@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Gave weapons critical strike {chance, multiplier} stats - Gave weapons critical strike {chance, multiplier} stats
- A system to add glow and reflection effects to figures (i.e: characters, armour, weapons, etc.) - A system to add glow and reflection effects to figures (i.e: characters, armour, weapons, etc.)
- Merchants will trade wares with players - Merchants will trade wares with players
- Airships that can be mounted and flown, and also walked on (`/airship` admin command)
### Changed ### Changed

View File

@ -141,15 +141,15 @@ impl InterpolatableComponent for Ori {
return self; return self;
} }
let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32; let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32;
let mut out = Slerp::slerp_unclamped(p0.0, p1.0, lerp_factor); let mut out = Slerp::slerp_unclamped(p0.to_quat(), p1.to_quat(), lerp_factor);
if out.into_vec4().map(|x| x.is_nan()).reduce_or() { if out.into_vec4().map(|x| x.is_nan()).reduce_or() {
warn!( warn!(
"interpolation output is nan: {}, {}, {:?}", "interpolation output is nan: {}, {}, {:?}",
t2, lerp_factor, buf t2, lerp_factor, buf
); );
out = p1.0; out = p1.to_quat();
} }
Ori(Slerp::slerp(self.0, out, PHYSICS_VS_EXTRAPOLATION_FACTOR).normalized()) Ori::new(Slerp::slerp(self.to_quat(), out, PHYSICS_VS_EXTRAPOLATION_FACTOR).normalized())
} }
} }

View File

@ -90,8 +90,9 @@ pub mod figuredata {
let mut colliders = HashMap::new(); let mut colliders = HashMap::new();
for (_, spec) in (manifest.read().0).0.iter() { for (_, spec) in (manifest.read().0).0.iter() {
for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() { for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() {
// TODO: avoid the requirement for symlinks in "voxygen.voxel.object.", and load // TODO: Currently both client and server load models and manifests from
// the models from "server.voxel." instead // "server.voxel.". In order to support CSG procedural airships, we probably
// need to load them in the server and sync them as an ECS resource.
let vox = let vox =
cache.load::<DotVoxAsset>(&["server.voxel.", &bone.central.0].concat())?; cache.load::<DotVoxAsset>(&["server.voxel.", &bone.central.0].concat())?;
let dyna = Dyna::<Cell, (), ColumnAccess>::from_vox(&vox.read().0, false); let dyna = Dyna::<Cell, (), ColumnAccess>::from_vox(&vox.read().0, false);
@ -117,7 +118,7 @@ pub mod figuredata {
} }
lazy_static! { lazy_static! {
// TODO: load this from the ECS as a resource, and maybe make it more general than ships // TODO: Load this from the ECS as a resource, and maybe make it more general than ships
// (although figuring out how to keep the figure bones in sync with the terrain offsets seems // (although figuring out how to keep the figure bones in sync with the terrain offsets seems
// like a hard problem if they're not the same manifest) // like a hard problem if they're not the same manifest)
pub static ref VOXEL_COLLIDER_MANIFEST: AssetHandle<ShipSpec> = AssetExt::load_expect("server.manifests.ship_manifest"); pub static ref VOXEL_COLLIDER_MANIFEST: AssetHandle<ShipSpec> = AssetExt::load_expect("server.manifests.ship_manifest");

View File

@ -9,7 +9,7 @@ use vek::{Quaternion, Vec2, Vec3};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(into = "SerdeOri")] #[serde(into = "SerdeOri")]
#[serde(from = "SerdeOri")] #[serde(from = "SerdeOri")]
pub struct Ori(pub Quaternion<f32>); pub struct Ori(Quaternion<f32>);
impl Default for Ori { impl Default for Ori {
/// Returns the default orientation (no rotation; default Dir) /// Returns the default orientation (no rotation; default Dir)

View File

@ -174,6 +174,7 @@ impl Body {
} }
} }
/// Returns flying speed if the body type can fly, otherwise None
pub fn can_fly(&self) -> Option<f32> { pub fn can_fly(&self) -> Option<f32> {
match self { match self {
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) => Some(1.0), Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) => Some(1.0),

View File

@ -35,7 +35,7 @@ impl Cylinder {
pub fn from_components( pub fn from_components(
pos: Vec3<f32>, pos: Vec3<f32>,
scale: Option<crate::comp::Scale>, scale: Option<crate::comp::Scale>,
collider: Option<crate::comp::Collider>, collider: Option<&crate::comp::Collider>,
char_state: Option<&crate::comp::CharacterState>, char_state: Option<&crate::comp::CharacterState>,
) -> Self { ) -> Self {
let scale = scale.map_or(1.0, |s| s.0); let scale = scale.map_or(1.0, |s| s.0);

View File

@ -108,7 +108,7 @@ pub struct PhysicsData<'a> {
impl<'a> PhysicsData<'a> { impl<'a> PhysicsData<'a> {
/// Add/reset physics state components /// Add/reset physics state components
fn reset(&mut self) { fn reset(&mut self) {
span!(guard, "Add/reset physics state components"); span!(_guard, "Add/reset physics state components");
for (entity, _, _, _, _) in ( for (entity, _, _, _, _) in (
&self.read.entities, &self.read.entities,
&self.read.colliders, &self.read.colliders,
@ -124,11 +124,10 @@ impl<'a> PhysicsData<'a> {
.entry(entity) .entry(entity)
.map(|e| e.or_insert_with(Default::default)); .map(|e| e.or_insert_with(Default::default));
} }
drop(guard);
} }
fn maintain_pushback_cache(&mut self) { fn maintain_pushback_cache(&mut self) {
span!(guard, "Maintain pushback cache"); span!(_guard, "Maintain pushback cache");
//Add PreviousPhysCache for all relevant entities //Add PreviousPhysCache for all relevant entities
for entity in ( for entity in (
&self.read.entities, &self.read.entities,
@ -188,11 +187,10 @@ impl<'a> PhysicsData<'a> {
phys_cache.scale = scale; phys_cache.scale = scale;
phys_cache.scaled_radius = flat_radius; phys_cache.scaled_radius = flat_radius;
} }
drop(guard);
} }
fn apply_pushback(&mut self, job: &mut Job<Sys>) { fn apply_pushback(&mut self, job: &mut Job<Sys>) {
span!(guard, "Apply pushback"); span!(_guard, "Apply pushback");
job.cpu_stats.measure(ParMode::Rayon); job.cpu_stats.measure(ParMode::Rayon);
let PhysicsData { let PhysicsData {
ref read, ref read,
@ -368,7 +366,6 @@ impl<'a> PhysicsData<'a> {
write.physics_metrics.entity_entity_collision_checks = write.physics_metrics.entity_entity_collision_checks =
metrics.entity_entity_collision_checks; metrics.entity_entity_collision_checks;
write.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions; write.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
drop(guard);
} }
fn handle_movement_and_terrain(&mut self, job: &mut Job<Sys>) { fn handle_movement_and_terrain(&mut self, job: &mut Job<Sys>) {
@ -465,7 +462,7 @@ impl<'a> PhysicsData<'a> {
_previous_cache, _previous_cache,
_, _,
)| { )| {
// defer the writes of positions and velocities to allow an inner loop over // Defer the writes of positions and velocities to allow an inner loop over
// terrain-like entities // terrain-like entities
let mut vel = *vel; let mut vel = *vel;
let old_vel = vel; let old_vel = vel;
@ -525,7 +522,7 @@ impl<'a> PhysicsData<'a> {
let mut cpos = *pos; let mut cpos = *pos;
let cylinder = (radius, z_min, z_max); let cylinder = (radius, z_min, z_max);
cylinder_voxel_collision( box_voxel_collision(
cylinder, cylinder,
&*read.terrain, &*read.terrain,
entity, entity,
@ -554,7 +551,7 @@ impl<'a> PhysicsData<'a> {
let cylinder = (radius, z_min, z_max); let cylinder = (radius, z_min, z_max);
let mut cpos = *pos; let mut cpos = *pos;
cylinder_voxel_collision( box_voxel_collision(
cylinder, cylinder,
&*read.terrain, &*read.terrain,
entity, entity,
@ -668,99 +665,99 @@ impl<'a> PhysicsData<'a> {
continue; continue;
} }
if let Collider::Voxel { id } = collider_other { let voxel_id = if let Collider::Voxel { id } = collider_other {
// use bounding cylinder regardless of our collider id
// TODO: extract point-terrain collision above to its own function } else {
let radius = collider.get_radius(); continue;
let (z_min, z_max) = collider.get_z_limits(1.0); };
// use bounding cylinder regardless of our collider
// TODO: extract point-terrain collision above to its own function
let radius = collider.get_radius();
let (z_min, z_max) = collider.get_z_limits(1.0);
let radius = radius.min(0.45) * scale; let radius = radius.min(0.45) * scale;
let z_min = z_min * scale; let z_min = z_min * scale;
let z_max = z_max.clamped(1.2, 1.95) * scale; let z_max = z_max.clamped(1.2, 1.95) * scale;
if let Some(voxel_collider) = if let Some(voxel_collider) =
VOXEL_COLLIDER_MANIFEST.read().colliders.get(&*id) VOXEL_COLLIDER_MANIFEST.read().colliders.get(&*voxel_id)
{ {
let mut physics_state_delta = physics_state.clone(); let mut physics_state_delta = physics_state.clone();
// deliberately don't use scale yet here, because the 11.0/0.8 // deliberately don't use scale yet here, because the 11.0/0.8
// thing is in the comp::Scale for visual reasons // thing is in the comp::Scale for visual reasons
let mut cpos = *pos; let mut cpos = *pos;
let wpos = cpos.0; let wpos = cpos.0;
// TODO: Cache the matrices here to avoid recomputing // TODO: Cache the matrices here to avoid recomputing
let transform_from = let transform_from = Mat4::<f32>::translation_3d(pos_other.0 - wpos)
Mat4::<f32>::translation_3d(pos_other.0 - wpos) * Mat4::from(ori_other.to_quat())
* Mat4::from(ori_other.0) * Mat4::<f32>::translation_3d(voxel_collider.translation);
* Mat4::<f32>::translation_3d(voxel_collider.translation); let transform_to = transform_from.inverted();
let transform_to = transform_from.inverted(); let ori_from = Mat4::from(ori_other.to_quat());
let ori_from = Mat4::from(ori_other.0); let ori_to = ori_from.inverted();
let ori_to = ori_from.inverted();
// The velocity of the collider, taking into account orientation. // The velocity of the collider, taking into account orientation.
let wpos_rel = (Mat4::<f32>::translation_3d(pos_other.0) let wpos_rel = (Mat4::<f32>::translation_3d(pos_other.0)
* Mat4::from(ori_other.0) * Mat4::from(ori_other.to_quat())
* Mat4::<f32>::translation_3d(voxel_collider.translation)) * Mat4::<f32>::translation_3d(voxel_collider.translation))
.inverted() .inverted()
.mul_point(wpos); .mul_point(wpos);
let wpos_last = (Mat4::<f32>::translation_3d(pos_other.0) let wpos_last = (Mat4::<f32>::translation_3d(pos_other.0)
* Mat4::from(previous_cache_other.ori) * Mat4::from(previous_cache_other.ori)
* Mat4::<f32>::translation_3d(voxel_collider.translation)) * Mat4::<f32>::translation_3d(voxel_collider.translation))
.mul_point(wpos_rel); .mul_point(wpos_rel);
let vel_other = vel_other.0 + (wpos - wpos_last) / read.dt.0; let vel_other = vel_other.0 + (wpos - wpos_last) / read.dt.0;
cpos.0 = transform_to.mul_point(Vec3::zero()); cpos.0 = transform_to.mul_point(Vec3::zero());
vel.0 = ori_to.mul_direction(vel.0 - vel_other); vel.0 = ori_to.mul_direction(vel.0 - vel_other);
let cylinder = (radius, z_min, z_max); let cylinder = (radius, z_min, z_max);
cylinder_voxel_collision( box_voxel_collision(
cylinder, cylinder,
&voxel_collider.dyna, &voxel_collider.dyna,
entity, entity,
&mut cpos, &mut cpos,
transform_to.mul_point(tgt_pos - wpos), transform_to.mul_point(tgt_pos - wpos),
&mut vel, &mut vel,
&mut physics_state_delta, &mut physics_state_delta,
ori_to.mul_direction(vel_other), ori_to.mul_direction(vel_other),
&read.dt, &read.dt,
was_on_ground, was_on_ground,
block_snap, block_snap,
climbing, climbing,
|entity, vel| { |entity, vel| {
land_on_grounds land_on_grounds
.push((entity, Vel(ori_from.mul_direction(vel.0)))) .push((entity, Vel(ori_from.mul_direction(vel.0))))
}, },
); );
cpos.0 = transform_from.mul_point(cpos.0) + wpos; cpos.0 = transform_from.mul_point(cpos.0) + wpos;
vel.0 = ori_from.mul_direction(vel.0) + vel_other; vel.0 = ori_from.mul_direction(vel.0) + vel_other;
tgt_pos = cpos.0; tgt_pos = cpos.0;
// union in the state updates, so that the state isn't just based on // union in the state updates, so that the state isn't just based on
// the most recent terrain that collision was attempted with // the most recent terrain that collision was attempted with
if physics_state_delta.on_ground { if physics_state_delta.on_ground {
physics_state.ground_vel = vel_other; physics_state.ground_vel = vel_other;
} }
physics_state.on_ground |= physics_state_delta.on_ground; physics_state.on_ground |= physics_state_delta.on_ground;
physics_state.on_ceiling |= physics_state_delta.on_ceiling; physics_state.on_ceiling |= physics_state_delta.on_ceiling;
physics_state.on_wall = physics_state.on_wall.or_else(|| { physics_state.on_wall = physics_state.on_wall.or_else(|| {
physics_state_delta physics_state_delta
.on_wall .on_wall
.map(|dir| ori_from.mul_direction(dir)) .map(|dir| ori_from.mul_direction(dir))
}); });
physics_state physics_state
.touch_entities .touch_entities
.append(&mut physics_state_delta.touch_entities); .append(&mut physics_state_delta.touch_entities);
physics_state.in_liquid = match ( physics_state.in_liquid =
physics_state.in_liquid, match (physics_state.in_liquid, physics_state_delta.in_liquid) {
physics_state_delta.in_liquid,
) {
// this match computes `x <|> y <|> liftA2 max x y` // this match computes `x <|> y <|> liftA2 max x y`
(Some(x), Some(y)) => Some(x.max(y)), (Some(x), Some(y)) => Some(x.max(y)),
(x @ Some(_), _) => x, (x @ Some(_), _) => x,
(_, y @ Some(_)) => y, (_, y @ Some(_)) => y,
_ => None, _ => None,
}; };
}
} }
} }
@ -804,7 +801,7 @@ impl<'a> PhysicsData<'a> {
for (ori, previous_phys_cache) in for (ori, previous_phys_cache) in
(&write.orientations, &mut write.previous_phys_cache).join() (&write.orientations, &mut write.previous_phys_cache).join()
{ {
previous_phys_cache.ori = ori.0; previous_phys_cache.ori = ori.to_quat();
} }
let mut event_emitter = read.event_bus.emitter(); let mut event_emitter = read.event_bus.emitter();
@ -845,7 +842,7 @@ impl<'a> System<'a> for Sys {
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn cylinder_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>( fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
cylinder: (f32, f32, f32), cylinder: (f32, f32, f32),
terrain: &'a T, terrain: &'a T,
entity: Entity, entity: Entity,

View File

@ -69,7 +69,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
find_dist::Cylinder::from_components( find_dist::Cylinder::from_components(
p.0, p.0,
scales.get(entity).copied(), scales.get(entity).copied(),
colliders.get(entity).cloned(), colliders.get(entity),
char_states.get(entity), char_states.get(entity),
) )
}) })

View File

@ -250,7 +250,7 @@ impl<'a> System<'a> for Sys {
let mut comp_sync_package = CompSyncPackage::new(); let mut comp_sync_package = CompSyncPackage::new();
let mut throttle = true; let mut throttle = true;
// extrapolation depends on receiving several frames indicating that something // Extrapolation depends on receiving several frames indicating that something
// has stopped in order for the extrapolated value to have // has stopped in order for the extrapolated value to have
// stopped // stopped
const SEND_UNCHANGED_PHYSICS_DATA: bool = true; const SEND_UNCHANGED_PHYSICS_DATA: bool = true;

View File

@ -1562,7 +1562,7 @@ fn under_cursor(
let player_cylinder = Cylinder::from_components( let player_cylinder = Cylinder::from_components(
player_pos, player_pos,
scales.get(player_entity).copied(), scales.get(player_entity).copied(),
colliders.get(player_entity).cloned(), colliders.get(player_entity),
char_states.get(player_entity), char_states.get(player_entity),
); );
let terrain = client.state().terrain(); let terrain = client.state().terrain();
@ -1643,7 +1643,7 @@ fn under_cursor(
let target_cylinder = Cylinder::from_components( let target_cylinder = Cylinder::from_components(
p, p,
scales.get(*e).copied(), scales.get(*e).copied(),
colliders.get(*e).cloned(), colliders.get(*e),
char_states.get(*e), char_states.get(*e),
); );
@ -1706,7 +1706,7 @@ fn select_interactable(
let player_cylinder = Cylinder::from_components( let player_cylinder = Cylinder::from_components(
player_pos, player_pos,
scales.get(player_entity).copied(), scales.get(player_entity).copied(),
colliders.get(player_entity).cloned(), colliders.get(player_entity),
char_states.get(player_entity), char_states.get(player_entity),
); );
@ -1720,7 +1720,7 @@ fn select_interactable(
.join() .join()
.filter(|(e, _, _, _, _)| *e != player_entity) .filter(|(e, _, _, _, _)| *e != player_entity)
.map(|(e, p, s, c, cs)| { .map(|(e, p, s, c, cs)| {
let cylinder = Cylinder::from_components(p.0, s.copied(), c.cloned(), cs); let cylinder = Cylinder::from_components(p.0, s.copied(), c, cs);
(e, cylinder) (e, cylinder)
}) })
// Roughly filter out entities farther than interaction distance // Roughly filter out entities farther than interaction distance