Correctly scale glider physics

This commit is contained in:
Joshua Barretto 2022-08-12 13:15:55 +01:00
parent df63e41a23
commit 587996abb7
7 changed files with 41 additions and 20 deletions

View File

@ -730,7 +730,7 @@ impl ServerChatCommand {
cmd(vec![], "Lightning strike at current position", Some(Admin))
},
ServerChatCommand::Scale => {
cmd(vec![Float("factor", 1.0, Required)], "Scale your character", Some(Admin))
cmd(vec![Float("factor", 1.0, Required), Boolean("reset_mass", true.to_string(), Optional)], "Scale your character", Some(Admin))
},
}
}

View File

@ -135,6 +135,7 @@ impl Body {
rel_flow: &Vel,
fluid_density: f32,
wings: Option<&Wings>,
scale: f32,
) -> Vec3<f32> {
let v_sq = rel_flow.0.magnitude_squared();
if v_sq < 0.25 {
@ -201,11 +202,11 @@ impl Body {
debug_assert!(c_d.is_sign_positive());
debug_assert!(c_l.is_sign_positive() || aoa.is_sign_negative());
planform_area * (c_l * *lift_dir + c_d * *rel_flow_dir)
+ self.parasite_drag() * *rel_flow_dir
planform_area * scale.powf(2.0) * (c_l * *lift_dir + c_d * *rel_flow_dir)
+ self.parasite_drag(scale) * *rel_flow_dir
},
_ => self.parasite_drag() * *rel_flow_dir,
_ => self.parasite_drag(scale) * *rel_flow_dir,
}
}
}
@ -214,13 +215,13 @@ impl Body {
/// Skin friction is the drag arising from the shear forces between a fluid
/// and a surface, while pressure drag is due to flow separation. Both are
/// viscous effects.
fn parasite_drag(&self) -> f32 {
fn parasite_drag(&self, scale: f32) -> f32 {
// Reference area and drag coefficient assumes best-case scenario of the
// orientation producing least amount of drag
match self {
// Cross-section, head/feet first
Body::BipedLarge(_) | Body::BipedSmall(_) | Body::Golem(_) | Body::Humanoid(_) => {
let dim = self.dimensions().xy().map(|a| a * 0.5);
let dim = self.dimensions().xy().map(|a| a * 0.5 * scale);
const CD: f32 = 0.7;
CD * PI * dim.x * dim.y
},
@ -231,7 +232,7 @@ impl Body {
| Body::QuadrupedSmall(_)
| Body::QuadrupedLow(_)
| Body::Arthropod(_) => {
let dim = self.dimensions().map(|a| a * 0.5);
let dim = self.dimensions().map(|a| a * 0.5 * scale);
let cd: f32 = if matches!(self, Body::QuadrupedLow(_)) {
0.7
} else {
@ -242,7 +243,7 @@ impl Body {
// Cross-section, zero-lift angle; exclude the wings (width * 0.2)
Body::BirdMedium(_) | Body::BirdLarge(_) | Body::Dragon(_) => {
let dim = self.dimensions().map(|a| a * 0.5);
let dim = self.dimensions().map(|a| a * 0.5 * scale);
let cd: f32 = match self {
// "Field Estimates of Body Drag Coefficient
// on the Basis of Dives in Passerine Birds",
@ -256,7 +257,7 @@ impl Body {
// Cross-section, zero-lift angle; exclude the fins (width * 0.2)
Body::FishMedium(_) | Body::FishSmall(_) => {
let dim = self.dimensions().map(|a| a * 0.5);
let dim = self.dimensions().map(|a| a * 0.5 * scale);
// "A Simple Method to Determine Drag Coefficients in Aquatic Animals",
// D. Bilo and W. Nachtigall, 1980
const CD: f32 = 0.031;
@ -276,7 +277,7 @@ impl Body {
| object::Body::FireworkYellow
| object::Body::MultiArrow
| object::Body::Dart => {
let dim = self.dimensions().map(|a| a * 0.5);
let dim = self.dimensions().map(|a| a * 0.5 * scale);
const CD: f32 = 0.02;
CD * PI * dim.x * dim.z
},
@ -295,20 +296,20 @@ impl Body {
| object::Body::Pumpkin3
| object::Body::Pumpkin4
| object::Body::Pumpkin5 => {
let dim = self.dimensions().map(|a| a * 0.5);
let dim = self.dimensions().map(|a| a * 0.5 * scale);
const CD: f32 = 0.5;
CD * PI * dim.x * dim.z
},
_ => {
let dim = self.dimensions();
let dim = self.dimensions().map(|a| a * scale);
const CD: f32 = 2.0;
CD * (PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0)
},
},
Body::ItemDrop(_) => {
let dim = self.dimensions();
let dim = self.dimensions().map(|a| a * scale);
const CD: f32 = 2.0;
CD * (PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0)
},
@ -316,7 +317,7 @@ impl Body {
Body::Ship(_) => {
// Airships tend to use the square of the cube root of its volume for
// reference area
let dim = self.dimensions();
let dim = self.dimensions().map(|a| a * scale);
(PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0)
},
}

View File

@ -76,7 +76,7 @@ impl CharacterBehavior for Data {
// They've climbed atop something, give them a boost
output_events.emit_local(LocalEvent::Jump(
data.entity,
CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0,
CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0 * data.scale.map_or(1.0, |s| s.0.powf(13.0).powf(0.25)),
));
};
update.character = CharacterState::Idle(idle::Data::default());

View File

@ -1075,7 +1075,7 @@ pub fn handle_jump(
output_events.emit_local(LocalEvent::Jump(
data.entity,
strength * impulse / data.mass.0
* data.scale.map_or(1.0, |s| s.0.powf(0.25))
* data.scale.map_or(1.0, |s| s.0.powf(13.0).powf(0.25))
* data.stats.move_speed_modifier,
));
})

View File

@ -50,6 +50,7 @@ fn integrate_forces(
mass: &Mass,
fluid: &Fluid,
gravity: f32,
scale: Option<Scale>,
) -> Vel {
let dim = body.dimensions();
let height = dim.z;
@ -61,7 +62,7 @@ fn integrate_forces(
// Aerodynamic/hydrodynamic forces
if !rel_flow.0.is_approx_zero() {
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, wings);
let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, wings, scale.map_or(1.0, |s| s.0));
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
if !impulse.is_approx_zero() {
let new_v = vel.0 + impulse / mass.0;
@ -610,6 +611,7 @@ impl<'a> PhysicsData<'a> {
&write.physics_states,
&read.masses,
&read.densities,
read.scales.maybe(),
!&read.is_ridings,
)
.par_join()
@ -628,6 +630,7 @@ impl<'a> PhysicsData<'a> {
physics_state,
mass,
density,
scale,
_,
)| {
let in_loaded_chunk = read
@ -672,6 +675,7 @@ impl<'a> PhysicsData<'a> {
mass,
&fluid,
GRAVITY,
scale.copied(),
)
.0
},
@ -1438,7 +1442,8 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
// Don't jump too far at once
const MAX_INCREMENTS: usize = 100; // The maximum number of collision tests per tick
let increments = ((pos_delta.map(|e| e.abs()).reduce_partial_max() / 0.3).ceil() as usize)
let min_step = (radius / 2.0).min(z_max - z_min).clamped(0.01, 0.3);
let increments = ((pos_delta.map(|e| e.abs()).reduce_partial_max() / min_step).ceil() as usize)
.clamped(1, MAX_INCREMENTS);
let old_pos = pos.0;
for _ in 0..increments {

View File

@ -1228,6 +1228,7 @@ fn handle_spawn(
)
.with(comp::Vel(vel))
.with(opt_scale.map(comp::Scale).unwrap_or(body.scale()))
.maybe_with(opt_scale.map(|s| comp::Mass(body.mass().0 * s.powi(3))))
.with(alignment);
if ai {
@ -3844,13 +3845,27 @@ fn handle_scale(
args: Vec<String>,
action: &ServerChatCommand,
) -> CmdResult<()> {
if let Some(scale) = parse_cmd_args!(args, f32) {
if let (Some(scale), reset_mass) = parse_cmd_args!(args, f32, bool) {
let scale = scale.clamped(0.025, 1000.0);
let _ = server
.state
.ecs_mut()
.write_storage::<comp::Scale>()
.insert(target, comp::Scale(scale));
if reset_mass.unwrap_or(true) {
if let Some(body) = server
.state
.ecs()
.read_storage::<comp::Body>()
.get(target)
{
let _ = server
.state
.ecs()
.write_storage()
.insert(target, comp::Mass(body.mass().0 * scale.powi(3)));
}
}
server.notify_client(
client,
ServerGeneral::server_msg(ChatType::CommandInfo, format!("Set scale to {}", scale)),

View File

@ -709,7 +709,7 @@ impl Server {
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() {
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(
&ecs.read_resource::<Arc<world::World>>(),
ecs.read_resource::<Arc<world::IndexOwned>>().as_index_ref(),
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
wpos,
old_block,
new_block,