From 884dc14c9f3302ea1a0ff7a8c08ccdc4f29c5216 Mon Sep 17 00:00:00 2001
From: Joe Ardent <code@ardent.nebcorp.com>
Date: Sun, 30 Mar 2025 16:41:03 -0700
Subject: [PATCH] split suspension and steering

---
 src/bike.rs    | 11 ++++--
 src/physics.rs | 91 +++++++++++++++++++++++++++++++-------------------
 2 files changed, 65 insertions(+), 37 deletions(-)

diff --git a/src/bike.rs b/src/bike.rs
index 7748563..9a6cd6a 100644
--- a/src/bike.rs
+++ b/src/bike.rs
@@ -26,9 +26,16 @@ pub enum CyberWheel {
 #[reflect(Component)]
 pub struct WheelState {
     pub displacement: Scalar,
+    pub force_normal: Scalar,
     pub sliding: bool,
-    pub grounded: bool,
-    pub ppos: Vec3,
+    pub contact_point: Option<Vec3>,
+    pub contact_normal: Option<Vec3>,
+}
+
+impl WheelState {
+    pub fn reset(&mut self) {
+        *self = Self::default();
+    }
 }
 
 #[derive(Component, Clone, Copy, Debug, Reflect)]
diff --git a/src/physics.rs b/src/physics.rs
index b580317..1b961b9 100644
--- a/src/physics.rs
+++ b/src/physics.rs
@@ -163,26 +163,15 @@ mod systems {
     }
 
     pub(super) fn suspension(
-        mut bike_body_query: Query<
-            (&Transform, &mut ExternalForce, RigidBodyQuery),
-            With<CyberBikeBody>,
-        >,
+        mut bike_body_query: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>,
         mut wheel_mesh_query: Query<
             (&mut Transform, &mut WheelState, &WheelConfig, &CyberWheel),
             Without<CyberBikeBody>,
         >,
         caster_query: Query<(&RayCaster, &RayHits, &CyberWheel)>,
         time: Res<Time>,
-        input: Res<InputState>,
         mut gizmos: Gizmos,
     ) {
-        let max_thrust = 100.0;
-        let max_yaw = 50.0;
-        let mut thrust = input.throttle * max_thrust;
-        if input.brake {
-            thrust *= -1.0;
-        }
-        let yaw = input.yaw * max_yaw;
         let dt = time.delta().as_secs_f32();
 
         let mut front_caster = &RayCaster::default();
@@ -204,10 +193,7 @@ mod systems {
             }
         }
 
-        let Ok((bike_xform, mut bike_forces, rbq)) = bike_body_query.get_single_mut() else {
-            bevy::log::warn!("Couldn't get body query");
-            return;
-        };
+        let (bike_xform, mut bike_forces) = bike_body_query.single_mut();
 
         for (mut xform, mut state, config, wheel) in wheel_mesh_query.iter_mut() {
             let (caster, hits) = match wheel {
@@ -217,48 +203,83 @@ mod systems {
 
             if let Some(hit) = hits.iter().next() {
                 let dist = hit.distance;
-                let cdir = caster.direction.as_vec3();
-                xform.translation = config.attach + (cdir * (dist - WHEEL_RADIUS));
+                let hit_point = caster.global_origin() + caster.global_direction() * dist;
 
                 let displacement = config.rest_dist - dist;
                 let damper_vel = (state.displacement - displacement) / dt;
-                state.displacement = displacement;
 
                 let mag = config.konstant * displacement - config.damping * damper_vel;
-
                 let mag = mag.max(0.0);
 
-                let fdir = hit.normal;
-                let force = fdir * mag;
+                state.force_normal = mag;
+                state.contact_normal = Some(hit.normal);
+                state.contact_point = Some(hit_point);
+                state.displacement = displacement;
 
-                let hit_point = caster.global_origin() + caster.global_direction() * dist;
+                let cdir = caster.direction.as_vec3();
+                xform.translation = config.attach + (cdir * (dist - WHEEL_RADIUS));
+
+                let force = hit.normal * mag;
                 gizmos.arrow(
                     hit_point,
                     hit_point + force,
                     Color::linear_rgb(1., 0.5, 0.2),
                 );
-
-                let _vel = rbq.velocity_at_point(hit_point);
-                //bevy::log::info!("{_vel:?}");
-
                 bike_forces.apply_force_at_point(
                     force,
                     caster.global_origin(),
                     bike_xform.translation,
                 );
+            } else {
+                xform.translation = config.attach + (caster.direction.as_vec3() * config.rest_dist);
+                state.reset();
+            }
+        }
+    }
 
-                let normal = hit.normal;
+    pub(super) fn steering(
+        mut bike_query: Query<
+            (&Transform, &mut ExternalForce, RigidBodyQueryReadOnly),
+            With<CyberBikeBody>,
+        >,
+        mut wheels: Query<(&mut WheelState, &WheelConfig, &CyberWheel)>,
+        time: Res<Time>,
+        input: Res<InputState>,
+        mut gizmos: Gizmos,
+    ) {
+        let Ok((bike_xform, mut bike_force, bike_body)) = bike_query.get_single_mut() else {
+            bevy::log::warn!("No entity for bike_query.");
+            return;
+        };
+
+        let max_thrust = 100.0;
+        let max_yaw = 50.0;
+        let thrust = input.throttle * max_thrust;
+        let yaw = input.yaw * max_yaw;
+        let dt = time.delta().as_secs_f32();
+
+        for (mut state, config, wheel) in wheels.iter_mut() {
+            if let Some(contact_point) = state.contact_point {
+                let vel = bike_body.velocity_at_point(contact_point);
+                bevy::log::info_once!("vel: {vel:?}");
+                let friction = config.friction;
+                bevy::log::info_once!(friction);
+                // just to shut the linter up about state not needing to be mut
+                state.sliding = true;
+
+                let normal = state.contact_normal.unwrap();
                 let steering = match wheel {
                     CyberWheel::Front => normal.cross(*bike_xform.back()) * yaw,
                     CyberWheel::Rear => Vec3::ZERO,
-                    //_ => normal.cross(*bike_xform.forward()) * yaw, // Vec3::ZERO,
                 };
                 let thrust = normal.cross(*bike_xform.right()) * thrust;
                 let total = (thrust + steering) * dt;
-                bike_forces.apply_force_at_point(total, hit_point, bike_xform.translation);
-                gizmos.arrow(hit_point, hit_point + total, Color::linear_rgb(1., 1., 0.2));
-            } else {
-                xform.translation = config.attach + (caster.direction.as_vec3() * config.rest_dist);
+                bike_force.apply_force_at_point(total, contact_point, bike_xform.translation);
+                gizmos.arrow(
+                    contact_point,
+                    contact_point + total,
+                    Color::linear_rgb(1., 1., 0.2),
+                );
             }
         }
     }
@@ -301,7 +322,7 @@ mod systems {
     }
 }
 
-use systems::{apply_lean, calculate_lean, suspension, tweak};
+use systems::{apply_lean, calculate_lean, steering, suspension, tweak};
 
 pub struct CyberPhysicsPlugin;
 
@@ -319,7 +340,7 @@ impl Plugin for CyberPhysicsPlugin {
             })
             .add_systems(
                 FixedUpdate,
-                (calculate_lean, apply_lean, suspension).chain(),
+                (calculate_lean, apply_lean, suspension, steering).chain(),
             )
             .add_systems(Update, tweak);
     }