|
@@ -30,6 +30,8 @@
|
|
|
|
|
|
#include "vehicle_body_3d.h"
|
|
#include "vehicle_body_3d.h"
|
|
|
|
|
|
|
|
+#include "core/config/engine.h"
|
|
|
|
+
|
|
#define ROLLING_INFLUENCE_FIX
|
|
#define ROLLING_INFLUENCE_FIX
|
|
|
|
|
|
class btVehicleJacobianEntry {
|
|
class btVehicleJacobianEntry {
|
|
@@ -78,6 +80,40 @@ public:
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+void VehicleWheel3D::FTIData::update_world_xform(Transform3D &r_xform, real_t p_interpolation_fraction) {
|
|
|
|
+ // Note that when unset (during the first few frames before a physics tick)
|
|
|
|
+ // the xform will be whatever it was loaded as.
|
|
|
|
+ if (!unset) {
|
|
|
|
+ real_t f = p_interpolation_fraction;
|
|
|
|
+
|
|
|
|
+ WheelXform i;
|
|
|
|
+ i.up = prev.up.lerp(curr.up, f);
|
|
|
|
+ i.up.normalize();
|
|
|
|
+ i.right = prev.right.lerp(curr.right, f);
|
|
|
|
+ i.right.normalize();
|
|
|
|
+
|
|
|
|
+ Vector3 fwd = i.up.cross(i.right);
|
|
|
|
+ fwd.normalize();
|
|
|
|
+
|
|
|
|
+ i.origin = prev.origin.lerp(curr.origin, f);
|
|
|
|
+ i.steering = Math::lerp(prev.steering, curr.steering, f);
|
|
|
|
+
|
|
|
|
+ real_t rotation = Math::lerp(curr_rotation - curr_rotation_delta, curr_rotation, f);
|
|
|
|
+
|
|
|
|
+ Basis steeringMat(i.up, i.steering);
|
|
|
|
+
|
|
|
|
+ Basis rotatingMat(i.right, rotation);
|
|
|
|
+
|
|
|
|
+ Basis basis2(
|
|
|
|
+ i.right[0], i.up[0], fwd[0],
|
|
|
|
+ i.right[1], i.up[1], fwd[1],
|
|
|
|
+ i.right[2], i.up[2], fwd[2]);
|
|
|
|
+
|
|
|
|
+ r_xform.set_basis(steeringMat * rotatingMat * basis2);
|
|
|
|
+ r_xform.set_origin(i.origin);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
void VehicleWheel3D::_notification(int p_what) {
|
|
void VehicleWheel3D::_notification(int p_what) {
|
|
switch (p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_ENTER_TREE: {
|
|
case NOTIFICATION_ENTER_TREE: {
|
|
@@ -381,6 +417,28 @@ void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState3D *s) {
|
|
Vector3 fwd = up.cross(right);
|
|
Vector3 fwd = up.cross(right);
|
|
fwd = fwd.normalized();
|
|
fwd = fwd.normalized();
|
|
|
|
|
|
|
|
+ VehicleWheel3D::FTIData &id = wheel.fti_data;
|
|
|
|
+
|
|
|
|
+ Vector3 origin = wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength;
|
|
|
|
+
|
|
|
|
+ if (is_physics_interpolated_and_enabled()) {
|
|
|
|
+ id.curr.up = up;
|
|
|
|
+ id.curr.right = right;
|
|
|
|
+ id.curr.steering = wheel.m_steering;
|
|
|
|
+ id.curr.origin = origin;
|
|
|
|
+
|
|
|
|
+ // Pump the xform the first update to the wheel,
|
|
|
|
+ // otherwise the world xform prev will be NULL.
|
|
|
|
+ if (id.unset || id.reset_queued) {
|
|
|
|
+ id.unset = false;
|
|
|
|
+ id.reset_queued = false;
|
|
|
|
+ id.pump();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // The physics etc relies on the rest of this being correct, even with FTI,
|
|
|
|
+ // so we can't pre-maturely return here.
|
|
|
|
+ }
|
|
|
|
+
|
|
Basis steeringMat(up, wheel.m_steering);
|
|
Basis steeringMat(up, wheel.m_steering);
|
|
|
|
|
|
Basis rotatingMat(right, wheel.m_rotation);
|
|
Basis rotatingMat(right, wheel.m_rotation);
|
|
@@ -392,8 +450,7 @@ void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState3D *s) {
|
|
|
|
|
|
wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2);
|
|
wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2);
|
|
//wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
|
|
//wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
|
|
- wheel.m_worldTransform.set_origin(
|
|
|
|
- wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength);
|
|
|
|
|
|
+ wheel.m_worldTransform.set_origin(origin);
|
|
}
|
|
}
|
|
|
|
|
|
real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
|
|
real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
|
|
@@ -817,6 +874,58 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void VehicleBody3D::_physics_interpolated_changed() {
|
|
|
|
+ _update_process_mode();
|
|
|
|
+ RigidBody3D::_physics_interpolated_changed();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void VehicleBody3D::fti_pump_xform() {
|
|
|
|
+ for (int i = 0; i < wheels.size(); i++) {
|
|
|
|
+ VehicleWheel3D &w = *wheels[i];
|
|
|
|
+ w.fti_data.pump();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RigidBody3D::fti_pump_xform();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void VehicleBody3D::_update_process_mode() {
|
|
|
|
+ set_process_internal(is_physics_interpolated_and_enabled());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void VehicleBody3D::_notification(int p_what) {
|
|
|
|
+ switch (p_what) {
|
|
|
|
+ case NOTIFICATION_ENTER_TREE: {
|
|
|
|
+ _update_process_mode();
|
|
|
|
+ } break;
|
|
|
|
+ case NOTIFICATION_INTERNAL_PROCESS: {
|
|
|
|
+#ifdef DEV_ENABLED
|
|
|
|
+ if (!is_physics_interpolated_and_enabled()) {
|
|
|
|
+ WARN_PRINT_ONCE("VehicleBody NOTIFICATION_INTERNAL_PROCESS with physics interpolation OFF. (benign)");
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
|
|
|
|
+
|
|
|
|
+ Transform3D xform;
|
|
|
|
+ Transform3D inv_vehicle_xform = get_global_transform_interpolated().affine_inverse();
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < wheels.size(); i++) {
|
|
|
|
+ VehicleWheel3D &w = *wheels[i];
|
|
|
|
+ w.fti_data.update_world_xform(xform, f);
|
|
|
|
+ w.set_transform(inv_vehicle_xform * xform);
|
|
|
|
+ }
|
|
|
|
+ } break;
|
|
|
|
+ case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
|
|
|
+ _update_process_mode();
|
|
|
|
+ for (int i = 0; i < wheels.size(); i++) {
|
|
|
|
+ VehicleWheel3D &w = *wheels[i];
|
|
|
|
+ w.fti_data.reset_queued = true;
|
|
|
|
+ }
|
|
|
|
+ } break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
|
|
void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
|
|
RigidBody3D::_body_state_changed(p_state);
|
|
RigidBody3D::_body_state_changed(p_state);
|
|
|
|
|
|
@@ -826,9 +935,14 @@ void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
|
|
_update_wheel(i, p_state);
|
|
_update_wheel(i, p_state);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ bool use_fti = is_physics_interpolated_and_enabled();
|
|
|
|
+
|
|
for (int i = 0; i < wheels.size(); i++) {
|
|
for (int i = 0; i < wheels.size(); i++) {
|
|
_ray_cast(i, p_state);
|
|
_ray_cast(i, p_state);
|
|
- wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform);
|
|
|
|
|
|
+ if (!use_fti) {
|
|
|
|
+ // TODO: can this path also just use world space directly instead of inverse parent space?
|
|
|
|
+ wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
_update_suspension(p_state);
|
|
_update_suspension(p_state);
|
|
@@ -880,6 +994,9 @@ void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
|
|
}
|
|
}
|
|
|
|
|
|
wheel.m_rotation += wheel.m_deltaRotation;
|
|
wheel.m_rotation += wheel.m_deltaRotation;
|
|
|
|
+ if (use_fti) {
|
|
|
|
+ wheel.fti_data.rotate(wheel.m_deltaRotation);
|
|
|
|
+ }
|
|
wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math::TAU;
|
|
wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math::TAU;
|
|
|
|
|
|
wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact
|
|
wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact
|