mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement lift and adapt things to work with it
This reverts commit 823d38a058e757542ac423af5da457ca02cce674.
This commit is contained in:
parent
b198109110
commit
dcec45075d
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5463,6 +5463,7 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
"image",
|
"image",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"inline_tweak",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -5541,6 +5542,7 @@ dependencies = [
|
|||||||
"bincode",
|
"bincode",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"inline_tweak",
|
||||||
"ordered-float 2.1.1",
|
"ordered-float 2.1.1",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
@ -1192,7 +1192,7 @@ impl Client {
|
|||||||
.map(|cs| {
|
.map(|cs| {
|
||||||
matches!(
|
matches!(
|
||||||
cs,
|
cs,
|
||||||
comp::CharacterState::GlideWield | comp::CharacterState::Glide
|
comp::CharacterState::GlideWield | comp::CharacterState::Glide(_)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ default = ["simd"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
common-base = { package = "veloren-common-base", path = "base" }
|
common-base = { package = "veloren-common-base", path = "base" }
|
||||||
# inline_tweak = "1.0.8"
|
inline_tweak = "1.0.8"
|
||||||
|
|
||||||
# Serde
|
# Serde
|
||||||
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||||
|
@ -595,6 +595,21 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wings(&self) -> Option<RigidWings> {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Body::BirdMedium(_)
|
||||||
|
| Body::BirdSmall(_)
|
||||||
|
| Body::Dragon(_)
|
||||||
|
| Body::FishMedium(_)
|
||||||
|
| Body::FishSmall(_)
|
||||||
|
)
|
||||||
|
.then_some({
|
||||||
|
let dim = self.dimensions().xy();
|
||||||
|
RigidWings::new(dim.x, dim.y * 0.2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
||||||
match buff {
|
match buff {
|
||||||
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
||||||
@ -651,3 +666,30 @@ impl Body {
|
|||||||
impl Component for Body {
|
impl Component for Body {
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rigid, elliptical wings
|
||||||
|
// Not exactly great for birds and such, but for now it'll have to do.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RigidWings {
|
||||||
|
aspect_ratio: f32,
|
||||||
|
planform_area: f32,
|
||||||
|
// sweep_angle: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RigidWings {
|
||||||
|
/// Wings from total span (wing-tip to wing-tip) and
|
||||||
|
/// chord length (leading edge to trailing edge)
|
||||||
|
pub fn new(span_length: f32, chord_length: f32) -> Self {
|
||||||
|
let planform_area = std::f32::consts::PI * chord_length * span_length * 0.25;
|
||||||
|
Self {
|
||||||
|
aspect_ratio: span_length.powi(2) / planform_area,
|
||||||
|
planform_area,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The aspect ratio is the ratio of the span squared to actual planform
|
||||||
|
/// area
|
||||||
|
pub fn aspect_ratio(&self) -> f32 { self.aspect_ratio }
|
||||||
|
|
||||||
|
pub fn planform_area(&self) -> f32 { self.planform_area }
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ pub enum CharacterState {
|
|||||||
Dance,
|
Dance,
|
||||||
Talk,
|
Talk,
|
||||||
Sneak,
|
Sneak,
|
||||||
Glide,
|
Glide(glide::Data),
|
||||||
GlideWield,
|
GlideWield,
|
||||||
/// A stunned state
|
/// A stunned state
|
||||||
Stunned(stunned::Data),
|
Stunned(stunned::Data),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
body::{object, Body},
|
body::{object, Body, RigidWings},
|
||||||
Density, Vel,
|
Density, Ori, Vel,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{AIR_DENSITY, WATER_DENSITY},
|
consts::{AIR_DENSITY, WATER_DENSITY},
|
||||||
@ -88,7 +88,13 @@ impl Default for Fluid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn aerodynamic_forces(&self, rel_flow: &Vel, fluid_density: f32) -> Vec3<f32> {
|
pub fn aerodynamic_forces(
|
||||||
|
&self,
|
||||||
|
ori: &Ori,
|
||||||
|
rel_flow: &Vel,
|
||||||
|
fluid_density: f32,
|
||||||
|
wings: Option<&RigidWings>,
|
||||||
|
) -> Vec3<f32> {
|
||||||
let v_sq = rel_flow.0.magnitude_squared();
|
let v_sq = rel_flow.0.magnitude_squared();
|
||||||
if v_sq < 0.25 {
|
if v_sq < 0.25 {
|
||||||
// don't bother with miniscule forces
|
// don't bother with miniscule forces
|
||||||
@ -96,7 +102,69 @@ impl Body {
|
|||||||
} else {
|
} else {
|
||||||
let rel_flow_dir = Dir::new(rel_flow.0 / v_sq.sqrt());
|
let rel_flow_dir = Dir::new(rel_flow.0 / v_sq.sqrt());
|
||||||
// All the coefficients come pre-multiplied by their reference area
|
// All the coefficients come pre-multiplied by their reference area
|
||||||
0.5 * fluid_density * v_sq * self.parasite_drag_coefficient() * *rel_flow_dir
|
0.5 * fluid_density
|
||||||
|
* v_sq
|
||||||
|
* wings
|
||||||
|
.filter(|_| crate::lift_enabled())
|
||||||
|
.map(|wings| {
|
||||||
|
// Since we have wings, we proceed to calculate the lift and drag
|
||||||
|
|
||||||
|
let ar = wings.aspect_ratio();
|
||||||
|
// aoa will be positive when we're pitched up and negative otherwise
|
||||||
|
let aoa = angle_of_attack(ori, &rel_flow_dir);
|
||||||
|
// c_l will be positive when aoa is positive (we have positive lift,
|
||||||
|
// producing an upward force) and negative otherwise
|
||||||
|
let c_l = wings.lift_coefficient(aoa);
|
||||||
|
|
||||||
|
// lift dir will be orthogonal to the local relative flow vector.
|
||||||
|
// Local relative flow is the resulting vector of (relative) freestream flow
|
||||||
|
// + downwash (created by the vortices of the wing tips)
|
||||||
|
let lift_dir: Dir = {
|
||||||
|
// induced angle of attack
|
||||||
|
let aoa_i = c_l / (PI * ar);
|
||||||
|
// effective angle of attack; the aoa as seen by aerofoil after downwash
|
||||||
|
let aoa_eff = aoa - aoa_i;
|
||||||
|
/*println!(
|
||||||
|
"CL={:.1}, α={:.1}°, αᵢ={:.1}°, αₑ={:.1}°, AR={:.1}",
|
||||||
|
c_l,
|
||||||
|
aoa.to_degrees(),
|
||||||
|
aoa_i.to_degrees(),
|
||||||
|
aoa_eff.to_degrees(),
|
||||||
|
ar
|
||||||
|
);*/
|
||||||
|
// Angle between chord line and local relative wind is aoa_eff radians.
|
||||||
|
// Direction of lift is perpendicular to local relative wind.
|
||||||
|
// At positive lift, local relative wind will be below our cord line at
|
||||||
|
// an angle of aoa_eff. Thus if we pitch down by aoa_eff radians then
|
||||||
|
// our chord line will be colinear with local relative wind vector and
|
||||||
|
// our up will be the direction of lift.
|
||||||
|
ori.pitched_down(aoa_eff).up()
|
||||||
|
};
|
||||||
|
|
||||||
|
// drag coefficient due to lift
|
||||||
|
let c_d = {
|
||||||
|
// Oswald's efficiency factor (empirically derived--very magical)
|
||||||
|
// (this definition should not be used for aspect ratios > 25)
|
||||||
|
let e = 1.78 * (1.0 - 0.045 * ar.powf(0.68)) - 0.64;
|
||||||
|
|
||||||
|
wings.zero_lift_drag_coefficient()
|
||||||
|
+ self.parasite_drag_coefficient()
|
||||||
|
+ c_l.powi(2) / (PI * e * ar)
|
||||||
|
};
|
||||||
|
debug_assert!(c_d.is_sign_positive());
|
||||||
|
debug_assert!(c_l.is_sign_positive() || aoa.is_sign_negative());
|
||||||
|
/*println!(
|
||||||
|
"L/D (at α={:.1}, AR={:.1}) = {:.1}/{:.1} = {:.1}",
|
||||||
|
aoa.to_degrees(),
|
||||||
|
ar,
|
||||||
|
0.5 * fluid_density * v_sq * c_l,
|
||||||
|
0.5 * fluid_density * v_sq * c_d,
|
||||||
|
c_l / c_d
|
||||||
|
);*/
|
||||||
|
|
||||||
|
c_l * *lift_dir + c_d * *rel_flow_dir
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| self.parasite_drag_coefficient() * *rel_flow_dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +262,88 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Geometric angle of attack
|
||||||
|
fn angle_of_attack(ori: &Ori, rel_flow_dir: &Dir) -> f32 {
|
||||||
|
PI / 2.0 - ori.up().angle_between(rel_flow_dir.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RigidWings {
|
||||||
|
/// Total lift coefficient for a finite wing of symmetric aerofoil shape and
|
||||||
|
/// elliptical pressure distribution.
|
||||||
|
pub fn lift_coefficient(&self, aoa: f32) -> f32 {
|
||||||
|
let aoa_abs = aoa.abs();
|
||||||
|
let stall_angle = PI * 0.1;
|
||||||
|
inline_tweak::tweak!(1.0)
|
||||||
|
* self.planform_area()
|
||||||
|
* if aoa_abs < stall_angle {
|
||||||
|
self.lift_slope(None) * aoa
|
||||||
|
} else if inline_tweak::tweak!(true) {
|
||||||
|
// This is when flow separation and turbulence starts to kick in.
|
||||||
|
// Going to just make something up (based on some data), as the alternative is
|
||||||
|
// to just throw your hands up and return 0
|
||||||
|
let aoa_s = aoa.signum();
|
||||||
|
let c_l_max = self.lift_slope(None) * stall_angle;
|
||||||
|
let deg_45 = PI / 4.0;
|
||||||
|
if aoa_abs < deg_45 {
|
||||||
|
// drop directly to 0.6 * max lift at stall angle
|
||||||
|
// then climb back to max at 45°
|
||||||
|
Lerp::lerp(0.6 * c_l_max, c_l_max, aoa_abs / deg_45) * aoa_s
|
||||||
|
} else {
|
||||||
|
// let's just say lift goes down linearly again until we're at 90°
|
||||||
|
Lerp::lerp(c_l_max, 0.0, (aoa_abs - deg_45) / deg_45) * aoa_s
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The zero-lift profile drag coefficient is the parasite drag on the wings
|
||||||
|
/// at the angle of attack which generates no lift
|
||||||
|
pub fn zero_lift_drag_coefficient(&self) -> f32 {
|
||||||
|
// avg value for Harris' hawk (Parabuteo unicinctus) [1]
|
||||||
|
self.planform_area() * 0.02
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The change in lift over change in angle of attack¹. Multiplying by angle
|
||||||
|
/// of attack gives the lift coefficient (for a finite wing, not aerofoil).
|
||||||
|
///
|
||||||
|
/// Aspect ratio is the ratio of total wing span squared over planform area.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// Only valid for symmetric, elliptical wings at small² angles of attack³.
|
||||||
|
/// Does not apply to twisted, cambered or delta wings. (It still gives a
|
||||||
|
/// reasonably accurate approximation if the wing shape is not truly
|
||||||
|
/// elliptical.)
|
||||||
|
///
|
||||||
|
/// 1. geometric angle of attack, i.e. the pitch angle relative to
|
||||||
|
/// freestream flow
|
||||||
|
/// 2. up to around ~18°, at which point maximum lift has been achieved and
|
||||||
|
/// thereafter falls precipitously, causing a stall (this is the stall
|
||||||
|
/// angle) 3. effective aoa, i.e. geometric aoa - induced aoa; assumes
|
||||||
|
/// no sideslip
|
||||||
|
fn lift_slope(&self, sweep_angle: Option<f32>) -> f32 {
|
||||||
|
// lift slope for a thin aerofoil, given by Thin Aerofoil Theory
|
||||||
|
let ar = self.aspect_ratio();
|
||||||
|
let a0 = 2.0 * PI;
|
||||||
|
if let Some(sweep) = sweep_angle {
|
||||||
|
// for swept wings we use Kuchemann's modification to Helmbold's
|
||||||
|
// equation
|
||||||
|
let a0_cos_sweep = a0 * sweep.cos();
|
||||||
|
let x = a0_cos_sweep / (PI * ar);
|
||||||
|
a0_cos_sweep / ((1.0 + x.powi(2)).sqrt() + x)
|
||||||
|
} else if ar < 4.0 {
|
||||||
|
// for low aspect ratio wings (AR < 4) we use Helmbold's equation
|
||||||
|
let x = a0 / (PI * ar);
|
||||||
|
a0 / ((1.0 + x.powi(2)).sqrt() + x)
|
||||||
|
} else {
|
||||||
|
// for high aspect ratio wings (AR > 4) we use the equation given by
|
||||||
|
// Prandtl's lifting-line theory
|
||||||
|
a0 / (1.0 + (a0 / (PI * ar)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
## References:
|
## References:
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ pub use self::{
|
|||||||
body::{
|
body::{
|
||||||
biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem,
|
biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem,
|
||||||
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
||||||
AllBodies, Body, BodyData,
|
AllBodies, Body, BodyData, RigidWings,
|
||||||
},
|
},
|
||||||
buff::{
|
buff::{
|
||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||||
|
@ -87,3 +87,5 @@ pub use comp::inventory::loadout_builder::LoadoutBuilder;
|
|||||||
pub use explosion::{Explosion, RadiusEffect};
|
pub use explosion::{Explosion, RadiusEffect};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use skillset_builder::SkillSetBuilder;
|
pub use skillset_builder::SkillSetBuilder;
|
||||||
|
|
||||||
|
pub fn lift_enabled() -> bool { inline_tweak::tweak!(true) }
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use super::utils::handle_climb;
|
use super::utils::handle_climb;
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{inventory::slot::EquipSlot, CharacterState, Ori, StateUpdate},
|
comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate},
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::{
|
||||||
|
behavior::{CharacterBehavior, JoinData},
|
||||||
|
utils::fly_move,
|
||||||
|
},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -12,7 +15,19 @@ const GLIDE_ACCEL: f32 = 5.0;
|
|||||||
const GLIDE_MAX_SPEED: f32 = 30.0;
|
const GLIDE_MAX_SPEED: f32 = 30.0;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Data;
|
pub struct Data {
|
||||||
|
pub wings: RigidWings,
|
||||||
|
pub ori: Ori,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn new(span_length: f32, chord_length: f32, ori: Ori) -> Self {
|
||||||
|
Self {
|
||||||
|
wings: RigidWings::new(span_length, chord_length),
|
||||||
|
ori,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CharacterBehavior for Data {
|
impl CharacterBehavior for Data {
|
||||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||||
@ -35,25 +50,29 @@ impl CharacterBehavior for Data {
|
|||||||
update.character = CharacterState::Idle
|
update.character = CharacterState::Idle
|
||||||
};
|
};
|
||||||
|
|
||||||
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
if crate::lift_enabled() {
|
||||||
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
fly_move(data, &mut update, inline_tweak::tweak!(0.1));
|
||||||
|
} else {
|
||||||
|
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
||||||
|
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
||||||
|
|
||||||
// Move player according to movement direction vector
|
// Move player according to movement direction vector
|
||||||
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) {
|
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) {
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine orientation vector from movement direction vector
|
// Determine orientation vector from movement direction vector
|
||||||
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
|
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
|
||||||
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
|
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply Glide antigrav lift
|
// Apply Glide antigrav lift
|
||||||
if update.vel.0.z < 0.0 {
|
if update.vel.0.z < 0.0 {
|
||||||
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
|
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
|
||||||
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
|
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
|
||||||
|
|
||||||
update.vel.0.z += lift * data.dt.0;
|
update.vel.0.z += lift * data.dt.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a wall in front of character and they are trying to climb go to
|
// If there is a wall in front of character and they are trying to climb go to
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use super::utils::*;
|
use super::utils::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{slot::EquipSlot, CharacterState, EnergySource, InventoryAction, StateUpdate},
|
comp::{slot::EquipSlot, CharacterState, EnergySource, InventoryAction, StateUpdate},
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::{
|
||||||
|
behavior::{CharacterBehavior, JoinData},
|
||||||
|
glide,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Data;
|
pub struct Data;
|
||||||
@ -24,7 +27,11 @@ impl CharacterBehavior for Data {
|
|||||||
.try_change_by(-energy_cost, EnergySource::Glide)
|
.try_change_by(-energy_cost, EnergySource::Glide)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Glide;
|
update.character = CharacterState::Glide(glide::Data::new(
|
||||||
|
inline_tweak::tweak!(10.0),
|
||||||
|
inline_tweak::tweak!(1.0),
|
||||||
|
*data.ori,
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
update.energy.set_to(0, EnergySource::Glide);
|
update.energy.set_to(0, EnergySource::Glide);
|
||||||
update.character = CharacterState::Idle;
|
update.character = CharacterState::Idle;
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
quadruped_low, quadruped_medium, quadruped_small, ship,
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||||
skills::{Skill, SwimSkill},
|
skills::{Skill, SwimSkill},
|
||||||
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||||
InventoryAction, StateUpdate,
|
InventoryAction, Ori, StateUpdate,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY},
|
consts::{FRIC_GROUND, GRAVITY},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
@ -367,18 +367,78 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, submers
|
|||||||
|
|
||||||
/// Updates components to move entity as if it's flying
|
/// Updates components to move entity as if it's flying
|
||||||
pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> bool {
|
pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> bool {
|
||||||
if let Some(force) = data.body.can_fly() {
|
let glider = match data.character {
|
||||||
|
CharacterState::Glide(data) => Some(data),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(force) = data
|
||||||
|
.body
|
||||||
|
.can_fly()
|
||||||
|
.or_else(|| glider.is_some().then_some(0.0))
|
||||||
|
{
|
||||||
let thrust = efficiency * force;
|
let thrust = efficiency * force;
|
||||||
|
|
||||||
let accel = thrust / data.mass.0;
|
let accel = thrust / data.mass.0;
|
||||||
|
|
||||||
handle_orientation(data, update, efficiency);
|
// if lift is enabled we do some more advanced stuff with pitch and roll
|
||||||
|
if crate::lift_enabled() && !matches!(data.body, Body::Ship(_)) {
|
||||||
|
let mut ori = glider.map(|g| g.ori).unwrap_or(update.ori);
|
||||||
|
let fw_dir = ori.look_dir().to_horizontal();
|
||||||
|
let tgt_ori = Some(data.inputs.move_dir)
|
||||||
|
.filter(|mv_dir| !mv_dir.is_approx_zero())
|
||||||
|
.map(|mv_dir| {
|
||||||
|
Vec3::new(
|
||||||
|
mv_dir.x,
|
||||||
|
mv_dir.y,
|
||||||
|
Lerp::lerp_unclamped(
|
||||||
|
0.0,
|
||||||
|
data.inputs.look_dir.z + inline_tweak::tweak!(0.3),
|
||||||
|
mv_dir.magnitude_squared() * inline_tweak::tweak!(2.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(Dir::from_unnormalized)
|
||||||
|
.and_then(|tgt_dir| {
|
||||||
|
Dir::from_unnormalized(data.vel.0)
|
||||||
|
.and_then(|moving_dir| moving_dir.to_horizontal())
|
||||||
|
.map(|moving_dir| {
|
||||||
|
Ori::from(tgt_dir).rolled_right(
|
||||||
|
(1.0 - moving_dir.dot(*tgt_dir).max(0.0))
|
||||||
|
* ori.right().dot(*tgt_dir).signum()
|
||||||
|
* std::f32::consts::PI
|
||||||
|
/ 3.0,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| fw_dir.map(Ori::from))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let rate = {
|
||||||
|
let angle = ori.look_dir().angle_between(*data.inputs.look_dir);
|
||||||
|
data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle
|
||||||
|
};
|
||||||
|
|
||||||
|
ori = ori.slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
|
||||||
|
if let Some(data) = glider {
|
||||||
|
update.character = CharacterState::Glide(glide::Data { ori, ..*data });
|
||||||
|
if let Some(char_ori) = ori.to_horizontal() {
|
||||||
|
update.ori = char_ori;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
update.ori = ori;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handle_orientation(data, update, efficiency);
|
||||||
|
}
|
||||||
|
|
||||||
// Elevation control
|
// Elevation control
|
||||||
match data.body {
|
match data.body {
|
||||||
// flappy flappy
|
// flappy flappy
|
||||||
Body::Dragon(_) | Body::BirdMedium(_) | Body::BirdSmall(_) => {
|
Body::Dragon(_) | Body::BirdMedium(_) | Body::BirdSmall(_) => {
|
||||||
let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0));
|
let anti_grav = if crate::lift_enabled() {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
GRAVITY * (1.0 + data.inputs.move_z.min(0.0))
|
||||||
|
};
|
||||||
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
|
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
|
||||||
},
|
},
|
||||||
// floaty floaty
|
// floaty floaty
|
||||||
|
@ -43,4 +43,4 @@ bincode = { version = "1.3.1", optional = true }
|
|||||||
plugin-api = { package = "veloren-plugin-api", path = "../../plugin/api", optional = true }
|
plugin-api = { package = "veloren-plugin-api", path = "../../plugin/api", optional = true }
|
||||||
|
|
||||||
# Tweak running code
|
# Tweak running code
|
||||||
# inline_tweak = { version = "1.0.8", features = ["release_tweak"] }
|
inline_tweak = { version = "1.0.8", features = ["release_tweak"] }
|
||||||
|
@ -287,7 +287,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::Idle => states::idle::Data.handle_event(&j, action),
|
CharacterState::Idle => states::idle::Data.handle_event(&j, action),
|
||||||
CharacterState::Talk => states::talk::Data.handle_event(&j, action),
|
CharacterState::Talk => states::talk::Data.handle_event(&j, action),
|
||||||
CharacterState::Climb(data) => data.handle_event(&j, action),
|
CharacterState::Climb(data) => data.handle_event(&j, action),
|
||||||
CharacterState::Glide => states::glide::Data.handle_event(&j, action),
|
CharacterState::Glide(data) => data.handle_event(&j, action),
|
||||||
CharacterState::GlideWield => {
|
CharacterState::GlideWield => {
|
||||||
states::glide_wield::Data.handle_event(&j, action)
|
states::glide_wield::Data.handle_event(&j, action)
|
||||||
},
|
},
|
||||||
@ -351,7 +351,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::Idle => states::idle::Data.behavior(&j),
|
CharacterState::Idle => states::idle::Data.behavior(&j),
|
||||||
CharacterState::Talk => states::talk::Data.behavior(&j),
|
CharacterState::Talk => states::talk::Data.behavior(&j),
|
||||||
CharacterState::Climb(data) => data.behavior(&j),
|
CharacterState::Climb(data) => data.behavior(&j),
|
||||||
CharacterState::Glide => states::glide::Data.behavior(&j),
|
CharacterState::Glide(data) => data.behavior(&j),
|
||||||
CharacterState::GlideWield => states::glide_wield::Data.behavior(&j),
|
CharacterState::GlideWield => states::glide_wield::Data.behavior(&j),
|
||||||
CharacterState::Stunned(data) => data.behavior(&j),
|
CharacterState::Stunned(data) => data.behavior(&j),
|
||||||
CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j),
|
CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j),
|
||||||
|
@ -13,6 +13,7 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
|
states,
|
||||||
terrain::{Block, TerrainGrid},
|
terrain::{Block, TerrainGrid},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Projection,
|
util::Projection,
|
||||||
@ -64,9 +65,11 @@ fn fluid_density(height: f32, fluid: &Fluid) -> Density {
|
|||||||
fn integrate_forces(
|
fn integrate_forces(
|
||||||
dt: &DeltaTime,
|
dt: &DeltaTime,
|
||||||
mut vel: Vel,
|
mut vel: Vel,
|
||||||
|
ori: &Ori,
|
||||||
body: &Body,
|
body: &Body,
|
||||||
density: &Density,
|
density: &Density,
|
||||||
mass: &Mass,
|
mass: &Mass,
|
||||||
|
character_state: Option<&CharacterState>,
|
||||||
fluid: &Fluid,
|
fluid: &Fluid,
|
||||||
gravity: f32,
|
gravity: f32,
|
||||||
) -> Vel {
|
) -> Vel {
|
||||||
@ -80,7 +83,24 @@ fn integrate_forces(
|
|||||||
// Aerodynamic/hydrodynamic forces
|
// Aerodynamic/hydrodynamic forces
|
||||||
if !rel_flow.0.is_approx_zero() {
|
if !rel_flow.0.is_approx_zero() {
|
||||||
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
|
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
|
||||||
let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0);
|
let glider: Option<&states::glide::Data> = character_state.and_then(|cs| match cs {
|
||||||
|
CharacterState::Glide(data) => Some(data),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
// let wings: Option<(RigidWings, Ori)> =
|
||||||
|
// body.wings().map(|w| (w, ori)).or(character_state.and_then(|cs| {
|
||||||
|
// match cs {
|
||||||
|
// CharacterState::Glide(states::glide::Data{wings, ori}) =>
|
||||||
|
// Some((*wings, *ori)), _ => None,
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
let impulse = dt.0
|
||||||
|
* body.aerodynamic_forces(
|
||||||
|
glider.map(|g| &g.ori).unwrap_or(ori),
|
||||||
|
&rel_flow,
|
||||||
|
fluid_density.0,
|
||||||
|
glider.map(|g| g.wings).or_else(|| body.wings()).as_ref(),
|
||||||
|
);
|
||||||
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
||||||
if !impulse.is_approx_zero() {
|
if !impulse.is_approx_zero() {
|
||||||
let v_sq = rel_flow.0.magnitude_squared();
|
let v_sq = rel_flow.0.magnitude_squared();
|
||||||
@ -97,7 +117,8 @@ fn integrate_forces(
|
|||||||
|
|
||||||
// Hydrostatic/aerostatic forces
|
// Hydrostatic/aerostatic forces
|
||||||
// modify gravity to account for the effective density as a result of buoyancy
|
// modify gravity to account for the effective density as a result of buoyancy
|
||||||
let down_force = dt.0 * gravity * (density.0 - fluid_density.0) / density.0;
|
let down_force =
|
||||||
|
dt.0 * inline_tweak::tweak!(1.0) * gravity * (density.0 - fluid_density.0) / density.0;
|
||||||
vel.0.z -= down_force;
|
vel.0.z -= down_force;
|
||||||
|
|
||||||
vel
|
vel
|
||||||
@ -578,7 +599,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
(
|
(
|
||||||
positions,
|
positions,
|
||||||
velocities,
|
velocities,
|
||||||
|
&write.orientations,
|
||||||
&read.bodies,
|
&read.bodies,
|
||||||
|
read.character_states.maybe(),
|
||||||
&write.physics_states,
|
&write.physics_states,
|
||||||
&read.masses,
|
&read.masses,
|
||||||
&read.densities,
|
&read.densities,
|
||||||
@ -590,7 +613,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
prof_span!(guard, "velocity update rayon job");
|
prof_span!(guard, "velocity update rayon job");
|
||||||
guard
|
guard
|
||||||
},
|
},
|
||||||
|_guard, (pos, vel, body, physics_state, mass, density, _)| {
|
|_guard, (pos, vel, ori, body, character_state, physics_state, mass, density, _)| {
|
||||||
let in_loaded_chunk = read
|
let in_loaded_chunk = read
|
||||||
.terrain
|
.terrain
|
||||||
.get_key(read.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
.get_key(read.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
||||||
@ -609,7 +632,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
},
|
},
|
||||||
Some(fluid) => {
|
Some(fluid) => {
|
||||||
vel.0 = integrate_forces(
|
vel.0 = integrate_forces(
|
||||||
&dt, *vel, body, density, mass, &fluid, GRAVITY,
|
&dt, *vel, ori, body, density, mass, character_state, &fluid, GRAVITY,
|
||||||
)
|
)
|
||||||
.0
|
.0
|
||||||
},
|
},
|
||||||
|
@ -221,7 +221,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
});
|
});
|
||||||
let is_gliding = matches!(
|
let is_gliding = matches!(
|
||||||
read_data.char_states.get(entity),
|
read_data.char_states.get(entity),
|
||||||
Some(CharacterState::GlideWield) | Some(CharacterState::Glide)
|
Some(CharacterState::GlideWield) | Some(CharacterState::Glide(_))
|
||||||
) && !physics_state.on_ground;
|
) && !physics_state.on_ground;
|
||||||
|
|
||||||
// This controls how picky NPCs are about their pathfinding. Giants are larger
|
// This controls how picky NPCs are about their pathfinding. Giants are larger
|
||||||
|
@ -11,8 +11,10 @@ type GlidingAnimationDependency = (
|
|||||||
Option<ToolKind>,
|
Option<ToolKind>,
|
||||||
Option<ToolKind>,
|
Option<ToolKind>,
|
||||||
Vec3<f32>,
|
Vec3<f32>,
|
||||||
Vec3<f32>,
|
Quaternion<f32>,
|
||||||
Vec3<f32>,
|
Quaternion<f32>,
|
||||||
|
Quaternion<f32>,
|
||||||
|
f32,
|
||||||
f32,
|
f32,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -27,90 +29,105 @@ impl Animation for GlidingAnimation {
|
|||||||
|
|
||||||
fn update_skeleton_inner(
|
fn update_skeleton_inner(
|
||||||
skeleton: &Self::Skeleton,
|
skeleton: &Self::Skeleton,
|
||||||
(_active_tool_kind, _second_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency,
|
(
|
||||||
|
_active_tool_kind,
|
||||||
|
_second_tool_kind,
|
||||||
|
velocity,
|
||||||
|
orientation,
|
||||||
|
last_ori,
|
||||||
|
glider_orientation,
|
||||||
|
global_time,
|
||||||
|
acc_vel,
|
||||||
|
): Self::Dependency,
|
||||||
anim_time: f32,
|
anim_time: f32,
|
||||||
_rate: &mut f32,
|
_rate: &mut f32,
|
||||||
s_a: &SkeletonAttr,
|
s_a: &SkeletonAttr,
|
||||||
) -> Self::Skeleton {
|
) -> Self::Skeleton {
|
||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
let speednorm = velocity.xy().magnitude().min(30.0) / 30.0;
|
||||||
|
let speedxyznorm = velocity.magnitude().min(30.0) / 30.0;
|
||||||
|
|
||||||
let quick = (anim_time * 7.0).sin();
|
let slow = (acc_vel * 0.5).sin();
|
||||||
let quicka = (anim_time * 7.0 + PI / 2.0).sin();
|
let slowa = (acc_vel * 0.5 + PI / 2.0).sin();
|
||||||
let wave_stop = (anim_time * 1.5).min(PI / 2.0).sin();
|
|
||||||
let slow = (anim_time * 3.0).sin();
|
|
||||||
let slowb = (anim_time * 3.0 + PI).sin();
|
|
||||||
let slowa = (anim_time * 3.0 + PI / 2.0).sin();
|
|
||||||
|
|
||||||
let head_look = Vec2::new(
|
let head_look = Vec2::new(
|
||||||
((global_time + anim_time) / 5.0).floor().mul(7331.0).sin() * 0.5,
|
((global_time + anim_time) as f32 / 4.0)
|
||||||
((global_time + anim_time) / 5.0).floor().mul(1337.0).sin() * 0.25,
|
.floor()
|
||||||
|
.mul(7331.0)
|
||||||
|
.sin()
|
||||||
|
* 0.5,
|
||||||
|
((global_time + anim_time) as f32 / 4.0)
|
||||||
|
.floor()
|
||||||
|
.mul(1337.0)
|
||||||
|
.sin()
|
||||||
|
* 0.25,
|
||||||
);
|
);
|
||||||
|
|
||||||
let ori: Vec2<f32> = Vec2::from(orientation);
|
let tilt = {
|
||||||
let last_ori = Vec2::from(last_ori);
|
let ori: Vec2<f32> = Vec2::from(orientation * Vec3::unit_y());
|
||||||
let tilt = if ::vek::Vec2::new(ori, last_ori)
|
let last_ori: Vec2<f32> = Vec2::from(last_ori * Vec3::unit_y());
|
||||||
.map(|o| o.magnitude_squared())
|
if ::vek::Vec2::new(ori, last_ori)
|
||||||
.map(|m| m > 0.0001 && m.is_finite())
|
.map(|o| o.magnitude_squared())
|
||||||
.reduce_and()
|
.map(|m| m > 0.001 && m.is_finite())
|
||||||
&& ori.angle_between(last_ori).is_finite()
|
.reduce_and()
|
||||||
{
|
&& ori.angle_between(last_ori).is_finite()
|
||||||
ori.angle_between(last_ori).min(0.05)
|
{
|
||||||
* last_ori.determine_side(Vec2::zero(), ori).signum()
|
ori.angle_between(last_ori).min(0.2)
|
||||||
} else {
|
* last_ori.determine_side(Vec2::zero(), ori).signum()
|
||||||
0.0
|
* 1.3
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
let torso_ori = Quaternion::slerp(
|
||||||
let tiltcancel = if anim_time > 1.0 { 1.0 } else { anim_time };
|
Quaternion::rotation_x(-0.06 * speednorm.max(5.0) + slow * 0.04)
|
||||||
|
* Quaternion::rotation_y(speednorm * tilt * 2.0 / speednorm.max(0.2))
|
||||||
next.head.position = Vec3::new(0.0, s_a.head.0 + 1.0, s_a.head.1);
|
* Quaternion::rotation_z(speednorm * tilt * 3.0 * speednorm),
|
||||||
next.head.orientation = Quaternion::rotation_x(0.35 - slow * 0.10 + head_look.y)
|
orientation.inverse() * glider_orientation,
|
||||||
* Quaternion::rotation_z(head_look.x + slowa * 0.15);
|
0.3,
|
||||||
|
|
||||||
next.chest.orientation = Quaternion::rotation_z(slowa * 0.02);
|
|
||||||
|
|
||||||
next.belt.orientation = Quaternion::rotation_z(slowa * 0.1 + tilt * tiltcancel * 12.0);
|
|
||||||
next.belt.position = Vec3::new(0.0, s_a.belt.0, s_a.belt.1);
|
|
||||||
|
|
||||||
next.shorts.orientation = Quaternion::rotation_z(slowa * 0.12 + tilt * tiltcancel * 16.0);
|
|
||||||
next.shorts.position = Vec3::new(0.0, s_a.shorts.0, s_a.shorts.1);
|
|
||||||
|
|
||||||
next.hand_l.position = Vec3::new(-9.5, -3.0, 10.0);
|
|
||||||
next.hand_l.orientation =
|
|
||||||
Quaternion::rotation_x(-2.7 + slowa * -0.1) * Quaternion::rotation_y(0.2);
|
|
||||||
|
|
||||||
next.hand_r.position = Vec3::new(9.5, -3.0, 10.0);
|
|
||||||
next.hand_r.orientation =
|
|
||||||
Quaternion::rotation_x(-2.7 + slowa * -0.10) * Quaternion::rotation_y(-0.2);
|
|
||||||
|
|
||||||
next.foot_l.position = Vec3::new(
|
|
||||||
-s_a.foot.0,
|
|
||||||
s_a.foot.1 + slowa * -1.0 + tilt * tiltcancel * -35.0,
|
|
||||||
-1.0 + s_a.foot.2,
|
|
||||||
);
|
);
|
||||||
next.foot_l.orientation = Quaternion::rotation_x(
|
let chest_ori = Quaternion::rotation_z(slowa * 0.01);
|
||||||
(wave_stop * -0.7 - quicka * -0.21 + slow * 0.19) * speed * 0.04,
|
let chest_global_inv = (orientation * torso_ori * chest_ori).inverse();
|
||||||
) * Quaternion::rotation_z(tilt * tiltcancel * 20.0);
|
let glider_pos = Vec3::new(0.0, -5.0, 18.0);
|
||||||
|
let glider_ori = chest_global_inv * glider_orientation;
|
||||||
|
let center_of_rot = glider_pos * 0.8;
|
||||||
|
|
||||||
next.foot_r.position = Vec3::new(
|
next.head.orientation = Quaternion::rotation_x(head_look.y + speednorm.min(28.0) * 0.03)
|
||||||
s_a.foot.0,
|
* Quaternion::rotation_z(head_look.x);
|
||||||
s_a.foot.1 + slowa * 1.0 + tilt * tiltcancel * 35.0,
|
|
||||||
-1.0 + s_a.foot.2,
|
|
||||||
);
|
|
||||||
next.foot_r.orientation = Quaternion::rotation_x(
|
|
||||||
(wave_stop * -0.8 + quick * -0.25 + slowb * 0.13) * speed * 0.04,
|
|
||||||
) * Quaternion::rotation_z(tilt * tiltcancel * 20.0);
|
|
||||||
|
|
||||||
next.glider.position = Vec3::new(0.0, -13.0 + slow * 0.10, 8.0);
|
next.torso.position =
|
||||||
next.glider.orientation =
|
(center_of_rot - orientation.inverse() * glider_orientation * center_of_rot) / 11.0
|
||||||
Quaternion::rotation_x(0.8) * Quaternion::rotation_y(slowa * 0.04);
|
* s_a.scaler;
|
||||||
|
next.torso.orientation = torso_ori;
|
||||||
|
|
||||||
|
next.chest.orientation = chest_ori;
|
||||||
|
|
||||||
|
next.belt.orientation = Quaternion::rotation_z(slowa * 0.1);
|
||||||
|
|
||||||
|
next.shorts.position = Vec3::new(s_a.shorts.0, 0.0, s_a.shorts.1);
|
||||||
|
next.shorts.orientation = chest_ori.inverse() * Quaternion::rotation_z(slowa * 0.12);
|
||||||
|
|
||||||
|
next.hand_l.position = glider_pos
|
||||||
|
+ glider_ori * Vec3::new(-s_a.hand.0 + -2.0, s_a.hand.1 + 8.0, s_a.hand.2 + -5.0);
|
||||||
|
next.hand_l.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(0.2);
|
||||||
|
|
||||||
|
next.hand_r.position = glider_pos
|
||||||
|
+ glider_ori * Vec3::new(s_a.hand.0 + 2.0, s_a.hand.1 + 8.0, s_a.hand.2 + -5.0);
|
||||||
|
next.hand_r.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(-0.2);
|
||||||
|
|
||||||
|
next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||||
|
next.foot_l.orientation =
|
||||||
|
Quaternion::rotation_x(-0.8 * speedxyznorm + slow * -0.5 * speedxyznorm);
|
||||||
|
|
||||||
|
next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||||
|
next.foot_r.orientation =
|
||||||
|
Quaternion::rotation_x(-0.8 * speedxyznorm + slow * 0.5 * speedxyznorm);
|
||||||
|
|
||||||
|
next.glider.position = glider_pos;
|
||||||
|
next.glider.orientation = glider_ori;
|
||||||
next.glider.scale = Vec3::one();
|
next.glider.scale = Vec3::one();
|
||||||
|
|
||||||
next.torso.position = Vec3::new(0.0, -1.0, 0.0) / 11.0 * s_a.scaler;
|
|
||||||
next.torso.orientation = Quaternion::rotation_x(-0.03 * speed.max(12.0) + slow * 0.04)
|
|
||||||
* Quaternion::rotation_y(tilt * tiltcancel * 32.0);
|
|
||||||
|
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1445,17 +1445,18 @@ impl FigureMgr {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CharacterState::Glide { .. } => {
|
CharacterState::Glide(data) => {
|
||||||
anim::character::GlidingAnimation::update_skeleton(
|
anim::character::GlidingAnimation::update_skeleton(
|
||||||
&target_base,
|
&target_base,
|
||||||
(
|
(
|
||||||
active_tool_kind,
|
active_tool_kind,
|
||||||
second_tool_kind,
|
second_tool_kind,
|
||||||
rel_vel,
|
rel_vel,
|
||||||
// TODO: Update to use the quaternion.
|
ori,
|
||||||
ori * anim::vek::Vec3::<f32>::unit_y(),
|
state.last_ori,
|
||||||
state.last_ori * anim::vek::Vec3::<f32>::unit_y(),
|
data.ori.into(),
|
||||||
time,
|
time,
|
||||||
|
state.acc_vel,
|
||||||
),
|
),
|
||||||
state.state_time,
|
state.state_time,
|
||||||
&mut state_animation_rate,
|
&mut state_animation_rate,
|
||||||
|
Loading…
Reference in New Issue
Block a user