use std::f32::consts::{FRAC_PI_3, FRAC_PI_4}; use avian3d::{ dynamics::solver::xpbd::AngularConstraint, parry::mass_properties::MassProperties, prelude::*, }; use bevy::prelude::{ Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without, }; //#[cfg(feature = "inspector")] //use super::ActionDebugInstant; use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings, Tunneling}; use crate::{ bike::{CyberBikeBody, CyberSteering, CyberWheel, WheelConfig, BIKE_WHEEL_COLLISION_GROUP}, input::InputState, }; fn yaw_to_angle(yaw: f32) -> f32 { let v = yaw.powi(5) * FRAC_PI_4; if v.is_normal() { v } else { 0.0 } } fn rotate_point(pt: &Vec3, rot: &Quat) -> Vec3 { // thanks to https://danceswithcode.net/engineeringnotes/quaternions/quaternions.html let [x, y, z] = pt.normalize().to_array(); let qpt = Quat::from_xyzw(x, y, z, 0.0); // p' = rot^-1 * qpt * rot let rot_qpt = rot.inverse() * qpt * *rot; // why does this need to be inverted??? -Vec3::from_array([rot_qpt.x, rot_qpt.y, rot_qpt.z]) } /// The gravity vector points from the cyberbike to the center of the planet. pub(super) fn gravity( mut query: Query<(&Transform, &mut ExternalForce), With>, settings: Res, mut gravity: ResMut, #[cfg(feature = "inspector")] mut debug_instant: ResMut, ) { let (xform, mut forces) = query.single_mut(); *gravity = Gravity(xform.translation.normalize() * -settings.gravity); forces.clear(); } /// The desired lean angle, given steering input and speed. pub(super) fn cyber_lean( bike_state: Query<(&LinearVelocity, &Transform), With>, wheels: Query<&Transform, With>, input: Res, gravity_settings: Res, mut lean: ResMut, ) { let (velocity, xform) = bike_state.single(); let vel = velocity.dot(*xform.forward()); let v_squared = vel.powi(2); let steering_angle = yaw_to_angle(input.yaw); let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect(); let wheel_base = (wheels[0] - wheels[1]).length(); let radius = wheel_base / steering_angle.tan(); let gravity = gravity_settings.gravity; let v2_r = v_squared / radius; let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3); if tan_theta.is_finite() && !tan_theta.is_subnormal() { lean.lean = tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4); } else { lean.lean = 0.0; } } /// PID-based controller for stabilizing attitude; keeps the cyberbike upright. pub(super) fn falling_cat( mut bike_query: Query<(&Transform, &mut ExternalTorque, &mut CatControllerState)>, time: Res