Skip to content

Commit 2428db2

Browse files
committed
Merge pull request #105915 from lawnjelly/fti_hotwheels4
FTI - Add custom interpolation for wheels
2 parents f4f1471 + a2b340a commit 2428db2

File tree

2 files changed

+166
-3
lines changed

2 files changed

+166
-3
lines changed

scene/3d/physics/vehicle_body_3d.cpp

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "vehicle_body_3d.h"
3232

33+
#include "core/config/engine.h"
34+
3335
#define ROLLING_INFLUENCE_FIX
3436

3537
class 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+
81117
void 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

399456
real_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+
820929
void 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

scene/3d/physics/vehicle_body_3d.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,44 @@ class VehicleWheel3D : public Node3D {
4040

4141
friend class VehicleBody3D;
4242

43+
struct WheelXform {
44+
Vector3 up;
45+
Vector3 right;
46+
Vector3 origin;
47+
real_t steering = 0;
48+
};
49+
50+
class FTIData {
51+
real_t curr_rotation = 0;
52+
real_t curr_rotation_delta = 0;
53+
54+
public:
55+
WheelXform curr;
56+
WheelXform prev;
57+
58+
// If a wheel is added on a frame, the xform will not be set until it has been physics updated at least once.
59+
bool unset = true;
60+
bool reset_queued = false;
61+
62+
void rotate(real_t p_delta) {
63+
curr_rotation += p_delta;
64+
curr_rotation_delta = p_delta;
65+
66+
// Wrap rotation to prevent float error.
67+
double wrapped = Math::fmod(curr_rotation + Math::PI, Math::TAU);
68+
if (wrapped < 0) {
69+
wrapped += Math::TAU;
70+
}
71+
curr_rotation = wrapped - Math::PI;
72+
}
73+
74+
void pump() {
75+
prev = curr;
76+
curr_rotation_delta = 0;
77+
}
78+
void update_world_xform(Transform3D &r_xform, real_t p_interpolation_fraction);
79+
} fti_data;
80+
4381
Transform3D m_worldTransform;
4482
Transform3D local_xform;
4583
bool engine_traction = false;
@@ -193,6 +231,8 @@ class VehicleBody3D : public RigidBody3D {
193231
void _update_wheel_transform(VehicleWheel3D &wheel, PhysicsDirectBodyState3D *s);
194232
void _update_wheel(int p_idx, PhysicsDirectBodyState3D *s);
195233

234+
void _update_process_mode();
235+
196236
friend class VehicleWheel3D;
197237
Vector<VehicleWheel3D *> wheels;
198238

@@ -201,6 +241,12 @@ class VehicleBody3D : public RigidBody3D {
201241
static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
202242
virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state) override;
203243

244+
protected:
245+
void _notification(int p_what);
246+
247+
virtual void _physics_interpolated_changed() override;
248+
virtual void fti_pump_xform() override;
249+
204250
public:
205251
void set_engine_force(real_t p_engine_force);
206252
real_t get_engine_force() const;

0 commit comments

Comments
 (0)