3030
3131#include " vehicle_body_3d.h"
3232
33+ #include " core/config/engine.h"
34+
3335#define ROLLING_INFLUENCE_FIX
3436
3537class btVehicleJacobianEntry {
@@ -78,6 +80,40 @@ class btVehicleJacobianEntry {
7880 }
7981};
8082
83+ void VehicleWheel3D::FTIData::update_world_xform (Transform3D &r_xform, real_t p_interpolation_fraction) {
84+ // Note that when unset (during the first few frames before a physics tick)
85+ // the xform will be whatever it was loaded as.
86+ if (!unset) {
87+ real_t f = p_interpolation_fraction;
88+
89+ WheelXform i;
90+ i.up = prev.up .lerp (curr.up , f);
91+ i.up .normalize ();
92+ i.right = prev.right .lerp (curr.right , f);
93+ i.right .normalize ();
94+
95+ Vector3 fwd = i.up .cross (i.right );
96+ fwd.normalize ();
97+
98+ i.origin = prev.origin .lerp (curr.origin , f);
99+ i.steering = Math::lerp (prev.steering , curr.steering , f);
100+
101+ real_t rotation = Math::lerp (curr_rotation - curr_rotation_delta, curr_rotation, f);
102+
103+ Basis steeringMat (i.up , i.steering );
104+
105+ Basis rotatingMat (i.right , rotation);
106+
107+ Basis basis2 (
108+ i.right [0 ], i.up [0 ], fwd[0 ],
109+ i.right [1 ], i.up [1 ], fwd[1 ],
110+ i.right [2 ], i.up [2 ], fwd[2 ]);
111+
112+ r_xform.set_basis (steeringMat * rotatingMat * basis2);
113+ r_xform.set_origin (i.origin );
114+ }
115+ }
116+
81117void VehicleWheel3D::_notification (int p_what) {
82118 switch (p_what) {
83119 case NOTIFICATION_ENTER_TREE: {
@@ -381,6 +417,28 @@ void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState3D *s) {
381417 Vector3 fwd = up.cross (right);
382418 fwd = fwd.normalized ();
383419
420+ VehicleWheel3D::FTIData &id = wheel.fti_data ;
421+
422+ Vector3 origin = wheel.m_raycastInfo .m_hardPointWS + wheel.m_raycastInfo .m_wheelDirectionWS * wheel.m_raycastInfo .m_suspensionLength ;
423+
424+ if (is_physics_interpolated_and_enabled ()) {
425+ id.curr .up = up;
426+ id.curr .right = right;
427+ id.curr .steering = wheel.m_steering ;
428+ id.curr .origin = origin;
429+
430+ // Pump the xform the first update to the wheel,
431+ // otherwise the world xform prev will be NULL.
432+ if (id.unset || id.reset_queued ) {
433+ id.unset = false ;
434+ id.reset_queued = false ;
435+ id.pump ();
436+ }
437+
438+ // The physics etc relies on the rest of this being correct, even with FTI,
439+ // so we can't pre-maturely return here.
440+ }
441+
384442 Basis steeringMat (up, wheel.m_steering );
385443
386444 Basis rotatingMat (right, wheel.m_rotation );
@@ -392,8 +450,7 @@ void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState3D *s) {
392450
393451 wheel.m_worldTransform .set_basis (steeringMat * rotatingMat * basis2);
394452 // wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
395- wheel.m_worldTransform .set_origin (
396- wheel.m_raycastInfo .m_hardPointWS + wheel.m_raycastInfo .m_wheelDirectionWS * wheel.m_raycastInfo .m_suspensionLength );
453+ wheel.m_worldTransform .set_origin (origin);
397454}
398455
399456real_t VehicleBody3D::_ray_cast (int p_idx, PhysicsDirectBodyState3D *s) {
@@ -817,6 +874,58 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) {
817874 }
818875}
819876
877+ void VehicleBody3D::_physics_interpolated_changed () {
878+ _update_process_mode ();
879+ RigidBody3D::_physics_interpolated_changed ();
880+ }
881+
882+ void VehicleBody3D::fti_pump_xform () {
883+ for (int i = 0 ; i < wheels.size (); i++) {
884+ VehicleWheel3D &w = *wheels[i];
885+ w.fti_data .pump ();
886+ }
887+
888+ RigidBody3D::fti_pump_xform ();
889+ }
890+
891+ void VehicleBody3D::_update_process_mode () {
892+ set_process_internal (is_physics_interpolated_and_enabled ());
893+ }
894+
895+ void VehicleBody3D::_notification (int p_what) {
896+ switch (p_what) {
897+ case NOTIFICATION_ENTER_TREE: {
898+ _update_process_mode ();
899+ } break ;
900+ case NOTIFICATION_INTERNAL_PROCESS: {
901+ #ifdef DEV_ENABLED
902+ if (!is_physics_interpolated_and_enabled ()) {
903+ WARN_PRINT_ONCE (" VehicleBody NOTIFICATION_INTERNAL_PROCESS with physics interpolation OFF. (benign)" );
904+ }
905+ #endif
906+ real_t f = Engine::get_singleton ()->get_physics_interpolation_fraction ();
907+
908+ Transform3D xform;
909+ Transform3D inv_vehicle_xform = get_global_transform_interpolated ().affine_inverse ();
910+
911+ for (int i = 0 ; i < wheels.size (); i++) {
912+ VehicleWheel3D &w = *wheels[i];
913+ w.fti_data .update_world_xform (xform, f);
914+ w.set_transform (inv_vehicle_xform * xform);
915+ }
916+ } break ;
917+ case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
918+ _update_process_mode ();
919+ for (int i = 0 ; i < wheels.size (); i++) {
920+ VehicleWheel3D &w = *wheels[i];
921+ w.fti_data .reset_queued = true ;
922+ }
923+ } break ;
924+ default :
925+ break ;
926+ }
927+ }
928+
820929void VehicleBody3D::_body_state_changed (PhysicsDirectBodyState3D *p_state) {
821930 RigidBody3D::_body_state_changed (p_state);
822931
@@ -826,9 +935,14 @@ void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
826935 _update_wheel (i, p_state);
827936 }
828937
938+ bool use_fti = is_physics_interpolated_and_enabled ();
939+
829940 for (int i = 0 ; i < wheels.size (); i++) {
830941 _ray_cast (i, p_state);
831- wheels[i]->set_transform (p_state->get_transform ().inverse () * wheels[i]->m_worldTransform );
942+ if (!use_fti) {
943+ // TODO: can this path also just use world space directly instead of inverse parent space?
944+ wheels[i]->set_transform (p_state->get_transform ().inverse () * wheels[i]->m_worldTransform );
945+ }
832946 }
833947
834948 _update_suspension (p_state);
@@ -880,6 +994,9 @@ void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
880994 }
881995
882996 wheel.m_rotation += wheel.m_deltaRotation ;
997+ if (use_fti) {
998+ wheel.fti_data .rotate (wheel.m_deltaRotation );
999+ }
8831000 wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60 ) / Math::TAU;
8841001
8851002 wheel.m_deltaRotation *= real_t (0.99 ); // damping of rotation when not in contact
0 commit comments