vehicle_body_3d.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /**************************************************************************/
  2. /* vehicle_body_3d.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "vehicle_body_3d.h"
  31. #include "core/config/engine.h"
  32. #define ROLLING_INFLUENCE_FIX
  33. class btVehicleJacobianEntry {
  34. public:
  35. Vector3 m_linearJointAxis;
  36. Vector3 m_aJ;
  37. Vector3 m_bJ;
  38. Vector3 m_0MinvJt;
  39. Vector3 m_1MinvJt;
  40. //Optimization: can be stored in the w/last component of one of the vectors
  41. real_t m_Adiag = 0.0;
  42. real_t getDiagonal() const { return m_Adiag; }
  43. btVehicleJacobianEntry() {}
  44. //constraint between two different rigidbodies
  45. btVehicleJacobianEntry(
  46. const Basis &world2A,
  47. const Basis &world2B,
  48. const Vector3 &rel_pos1,
  49. const Vector3 &rel_pos2,
  50. const Vector3 &jointAxis,
  51. const Vector3 &inertiaInvA,
  52. const real_t massInvA,
  53. const Vector3 &inertiaInvB,
  54. const real_t massInvB) :
  55. m_linearJointAxis(jointAxis) {
  56. m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis));
  57. m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis));
  58. m_0MinvJt = inertiaInvA * m_aJ;
  59. m_1MinvJt = inertiaInvB * m_bJ;
  60. m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ);
  61. //btAssert(m_Adiag > real_t(0.0));
  62. }
  63. real_t getRelativeVelocity(const Vector3 &linvelA, const Vector3 &angvelA, const Vector3 &linvelB, const Vector3 &angvelB) {
  64. Vector3 linrel = linvelA - linvelB;
  65. Vector3 angvela = angvelA * m_aJ;
  66. Vector3 angvelb = angvelB * m_bJ;
  67. linrel *= m_linearJointAxis;
  68. angvela += angvelb;
  69. angvela += linrel;
  70. real_t rel_vel2 = angvela[0] + angvela[1] + angvela[2];
  71. return rel_vel2 + CMP_EPSILON;
  72. }
  73. };
  74. void VehicleWheel3D::FTIData::update_world_xform(Transform3D &r_xform, real_t p_interpolation_fraction) {
  75. // Note that when unset (during the first few frames before a physics tick)
  76. // the xform will be whatever it was loaded as.
  77. if (!unset) {
  78. real_t f = p_interpolation_fraction;
  79. WheelXform i;
  80. i.up = prev.up.lerp(curr.up, f);
  81. i.up.normalize();
  82. i.right = prev.right.lerp(curr.right, f);
  83. i.right.normalize();
  84. Vector3 fwd = i.up.cross(i.right);
  85. fwd.normalize();
  86. i.origin = prev.origin.lerp(curr.origin, f);
  87. i.steering = Math::lerp(prev.steering, curr.steering, f);
  88. real_t rotation = Math::lerp(curr_rotation - curr_rotation_delta, curr_rotation, f);
  89. Basis steeringMat(i.up, i.steering);
  90. Basis rotatingMat(i.right, rotation);
  91. Basis basis2(
  92. i.right[0], i.up[0], fwd[0],
  93. i.right[1], i.up[1], fwd[1],
  94. i.right[2], i.up[2], fwd[2]);
  95. r_xform.set_basis(steeringMat * rotatingMat * basis2);
  96. r_xform.set_origin(i.origin);
  97. }
  98. }
  99. void VehicleWheel3D::_notification(int p_what) {
  100. switch (p_what) {
  101. case NOTIFICATION_ENTER_TREE: {
  102. VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
  103. if (!cb) {
  104. return;
  105. }
  106. body = cb;
  107. local_xform = get_transform();
  108. cb->wheels.push_back(this);
  109. m_chassisConnectionPointCS = get_transform().origin;
  110. m_wheelDirectionCS = -get_transform().basis.get_column(Vector3::AXIS_Y).normalized();
  111. m_wheelAxleCS = get_transform().basis.get_column(Vector3::AXIS_X).normalized();
  112. } break;
  113. case NOTIFICATION_EXIT_TREE: {
  114. VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
  115. if (!cb) {
  116. return;
  117. }
  118. cb->wheels.erase(this);
  119. body = nullptr;
  120. } break;
  121. }
  122. }
  123. PackedStringArray VehicleWheel3D::get_configuration_warnings() const {
  124. PackedStringArray warnings = Node3D::get_configuration_warnings();
  125. if (!Object::cast_to<VehicleBody3D>(get_parent())) {
  126. warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
  127. }
  128. return warnings;
  129. }
  130. void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) {
  131. if (m_raycastInfo.m_isInContact) {
  132. real_t project = m_raycastInfo.m_contactNormalWS.dot(m_raycastInfo.m_wheelDirectionWS);
  133. Vector3 chassis_velocity_at_contactPoint;
  134. Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin;
  135. chassis_velocity_at_contactPoint = s->get_linear_velocity() +
  136. (s->get_angular_velocity()).cross(relpos); // * mPos);
  137. real_t projVel = m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint);
  138. if (project >= real_t(-0.1)) {
  139. m_suspensionRelativeVelocity = real_t(0.0);
  140. m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
  141. } else {
  142. real_t inv = real_t(-1.) / project;
  143. m_suspensionRelativeVelocity = projVel * inv;
  144. m_clippedInvContactDotSuspension = inv;
  145. }
  146. } else { // Not in contact : position wheel in a nice (rest length) position
  147. m_raycastInfo.m_suspensionLength = m_suspensionRestLength;
  148. m_suspensionRelativeVelocity = real_t(0.0);
  149. m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS;
  150. m_clippedInvContactDotSuspension = real_t(1.0);
  151. }
  152. }
  153. void VehicleWheel3D::set_radius(real_t p_radius) {
  154. m_wheelRadius = p_radius;
  155. update_gizmos();
  156. }
  157. real_t VehicleWheel3D::get_radius() const {
  158. return m_wheelRadius;
  159. }
  160. void VehicleWheel3D::set_suspension_rest_length(real_t p_length) {
  161. m_suspensionRestLength = p_length;
  162. update_gizmos();
  163. }
  164. real_t VehicleWheel3D::get_suspension_rest_length() const {
  165. return m_suspensionRestLength;
  166. }
  167. void VehicleWheel3D::set_suspension_travel(real_t p_length) {
  168. m_maxSuspensionTravel = p_length;
  169. }
  170. real_t VehicleWheel3D::get_suspension_travel() const {
  171. return m_maxSuspensionTravel;
  172. }
  173. void VehicleWheel3D::set_suspension_stiffness(real_t p_value) {
  174. m_suspensionStiffness = p_value;
  175. }
  176. real_t VehicleWheel3D::get_suspension_stiffness() const {
  177. return m_suspensionStiffness;
  178. }
  179. void VehicleWheel3D::set_suspension_max_force(real_t p_value) {
  180. m_maxSuspensionForce = p_value;
  181. }
  182. real_t VehicleWheel3D::get_suspension_max_force() const {
  183. return m_maxSuspensionForce;
  184. }
  185. void VehicleWheel3D::set_damping_compression(real_t p_value) {
  186. m_wheelsDampingCompression = p_value;
  187. }
  188. real_t VehicleWheel3D::get_damping_compression() const {
  189. return m_wheelsDampingCompression;
  190. }
  191. void VehicleWheel3D::set_damping_relaxation(real_t p_value) {
  192. m_wheelsDampingRelaxation = p_value;
  193. }
  194. real_t VehicleWheel3D::get_damping_relaxation() const {
  195. return m_wheelsDampingRelaxation;
  196. }
  197. void VehicleWheel3D::set_friction_slip(real_t p_value) {
  198. m_frictionSlip = p_value;
  199. }
  200. real_t VehicleWheel3D::get_friction_slip() const {
  201. return m_frictionSlip;
  202. }
  203. void VehicleWheel3D::set_roll_influence(real_t p_value) {
  204. m_rollInfluence = p_value;
  205. }
  206. real_t VehicleWheel3D::get_roll_influence() const {
  207. return m_rollInfluence;
  208. }
  209. bool VehicleWheel3D::is_in_contact() const {
  210. return m_raycastInfo.m_isInContact;
  211. }
  212. Vector3 VehicleWheel3D::get_contact_point() const {
  213. return m_raycastInfo.m_contactPointWS;
  214. }
  215. Vector3 VehicleWheel3D::get_contact_normal() const {
  216. return m_raycastInfo.m_contactNormalWS;
  217. }
  218. Node3D *VehicleWheel3D::get_contact_body() const {
  219. return m_raycastInfo.m_groundObject;
  220. }
  221. void VehicleWheel3D::_bind_methods() {
  222. ClassDB::bind_method(D_METHOD("set_radius", "length"), &VehicleWheel3D::set_radius);
  223. ClassDB::bind_method(D_METHOD("get_radius"), &VehicleWheel3D::get_radius);
  224. ClassDB::bind_method(D_METHOD("set_suspension_rest_length", "length"), &VehicleWheel3D::set_suspension_rest_length);
  225. ClassDB::bind_method(D_METHOD("get_suspension_rest_length"), &VehicleWheel3D::get_suspension_rest_length);
  226. ClassDB::bind_method(D_METHOD("set_suspension_travel", "length"), &VehicleWheel3D::set_suspension_travel);
  227. ClassDB::bind_method(D_METHOD("get_suspension_travel"), &VehicleWheel3D::get_suspension_travel);
  228. ClassDB::bind_method(D_METHOD("set_suspension_stiffness", "length"), &VehicleWheel3D::set_suspension_stiffness);
  229. ClassDB::bind_method(D_METHOD("get_suspension_stiffness"), &VehicleWheel3D::get_suspension_stiffness);
  230. ClassDB::bind_method(D_METHOD("set_suspension_max_force", "length"), &VehicleWheel3D::set_suspension_max_force);
  231. ClassDB::bind_method(D_METHOD("get_suspension_max_force"), &VehicleWheel3D::get_suspension_max_force);
  232. ClassDB::bind_method(D_METHOD("set_damping_compression", "length"), &VehicleWheel3D::set_damping_compression);
  233. ClassDB::bind_method(D_METHOD("get_damping_compression"), &VehicleWheel3D::get_damping_compression);
  234. ClassDB::bind_method(D_METHOD("set_damping_relaxation", "length"), &VehicleWheel3D::set_damping_relaxation);
  235. ClassDB::bind_method(D_METHOD("get_damping_relaxation"), &VehicleWheel3D::get_damping_relaxation);
  236. ClassDB::bind_method(D_METHOD("set_use_as_traction", "enable"), &VehicleWheel3D::set_use_as_traction);
  237. ClassDB::bind_method(D_METHOD("is_used_as_traction"), &VehicleWheel3D::is_used_as_traction);
  238. ClassDB::bind_method(D_METHOD("set_use_as_steering", "enable"), &VehicleWheel3D::set_use_as_steering);
  239. ClassDB::bind_method(D_METHOD("is_used_as_steering"), &VehicleWheel3D::is_used_as_steering);
  240. ClassDB::bind_method(D_METHOD("set_friction_slip", "length"), &VehicleWheel3D::set_friction_slip);
  241. ClassDB::bind_method(D_METHOD("get_friction_slip"), &VehicleWheel3D::get_friction_slip);
  242. ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact);
  243. ClassDB::bind_method(D_METHOD("get_contact_body"), &VehicleWheel3D::get_contact_body);
  244. ClassDB::bind_method(D_METHOD("get_contact_point"), &VehicleWheel3D::get_contact_point);
  245. ClassDB::bind_method(D_METHOD("get_contact_normal"), &VehicleWheel3D::get_contact_normal);
  246. ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence);
  247. ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence);
  248. ClassDB::bind_method(D_METHOD("get_skidinfo"), &VehicleWheel3D::get_skidinfo);
  249. ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel3D::get_rpm);
  250. ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleWheel3D::set_engine_force);
  251. ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleWheel3D::get_engine_force);
  252. ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleWheel3D::set_brake);
  253. ClassDB::bind_method(D_METHOD("get_brake"), &VehicleWheel3D::get_brake);
  254. ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleWheel3D::set_steering);
  255. ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering);
  256. ADD_GROUP("Per-Wheel Motion", "");
  257. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, U"-1024,1024,0.01,or_less,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_engine_force", "get_engine_force");
  258. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, U"-128,128,0.01,or_less,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_brake", "get_brake");
  259. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180,0.01,radians_as_degrees"), "set_steering", "get_steering");
  260. ADD_GROUP("VehicleBody3D Motion", "");
  261. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction");
  262. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering");
  263. ADD_GROUP("Wheel", "wheel_");
  264. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_roll_influence"), "set_roll_influence", "get_roll_influence");
  265. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_radius", PROPERTY_HINT_NONE, "suffix:m"), "set_radius", "get_radius");
  266. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_rest_length", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_rest_length", "get_suspension_rest_length");
  267. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip");
  268. ADD_GROUP("Suspension", "suspension_");
  269. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_travel", "get_suspension_travel");
  270. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness", PROPERTY_HINT_NONE, U"suffix:N/mm"), "set_suspension_stiffness", "get_suspension_stiffness");
  271. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_suspension_max_force", "get_suspension_max_force");
  272. ADD_GROUP("Damping", "damping_");
  273. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_compression", "get_damping_compression");
  274. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_relaxation", "get_damping_relaxation");
  275. }
  276. void VehicleWheel3D::set_engine_force(real_t p_engine_force) {
  277. m_engineForce = p_engine_force;
  278. }
  279. real_t VehicleWheel3D::get_engine_force() const {
  280. return m_engineForce;
  281. }
  282. void VehicleWheel3D::set_brake(real_t p_brake) {
  283. m_brake = p_brake;
  284. }
  285. real_t VehicleWheel3D::get_brake() const {
  286. return m_brake;
  287. }
  288. void VehicleWheel3D::set_steering(real_t p_steering) {
  289. m_steering = p_steering;
  290. }
  291. real_t VehicleWheel3D::get_steering() const {
  292. return m_steering;
  293. }
  294. void VehicleWheel3D::set_use_as_traction(bool p_enable) {
  295. engine_traction = p_enable;
  296. }
  297. bool VehicleWheel3D::is_used_as_traction() const {
  298. return engine_traction;
  299. }
  300. void VehicleWheel3D::set_use_as_steering(bool p_enabled) {
  301. steers = p_enabled;
  302. }
  303. bool VehicleWheel3D::is_used_as_steering() const {
  304. return steers;
  305. }
  306. real_t VehicleWheel3D::get_skidinfo() const {
  307. return m_skidInfo;
  308. }
  309. real_t VehicleWheel3D::get_rpm() const {
  310. return m_rpm;
  311. }
  312. VehicleWheel3D::VehicleWheel3D() {
  313. set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF);
  314. }
  315. void VehicleBody3D::_update_wheel_transform(VehicleWheel3D &wheel, PhysicsDirectBodyState3D *s) {
  316. wheel.m_raycastInfo.m_isInContact = false;
  317. Transform3D chassisTrans = s->get_transform();
  318. /*
  319. if (interpolatedTransform && (getRigidBody()->getMotionState())) {
  320. getRigidBody()->getMotionState()->getWorldTransform(chassisTrans);
  321. }
  322. */
  323. wheel.m_raycastInfo.m_hardPointWS = chassisTrans.xform(wheel.m_chassisConnectionPointCS);
  324. //wheel.m_raycastInfo.m_hardPointWS+=s->get_linear_velocity()*s->get_step();
  325. wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.get_basis().xform(wheel.m_wheelDirectionCS).normalized();
  326. wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.get_basis().xform(wheel.m_wheelAxleCS).normalized();
  327. }
  328. void VehicleBody3D::_update_wheel(int p_idx, PhysicsDirectBodyState3D *s) {
  329. VehicleWheel3D &wheel = *wheels[p_idx];
  330. _update_wheel_transform(wheel, s);
  331. Vector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS;
  332. const Vector3 &right = wheel.m_raycastInfo.m_wheelAxleWS;
  333. Vector3 fwd = up.cross(right);
  334. fwd = fwd.normalized();
  335. VehicleWheel3D::FTIData &id = wheel.fti_data;
  336. Vector3 origin = wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength;
  337. if (is_physics_interpolated_and_enabled()) {
  338. id.curr.up = up;
  339. id.curr.right = right;
  340. id.curr.steering = wheel.m_steering;
  341. id.curr.origin = origin;
  342. // Pump the xform the first update to the wheel,
  343. // otherwise the world xform prev will be NULL.
  344. if (id.unset || id.reset_queued) {
  345. id.unset = false;
  346. id.reset_queued = false;
  347. id.pump();
  348. }
  349. // The physics etc relies on the rest of this being correct, even with FTI,
  350. // so we can't pre-maturely return here.
  351. }
  352. Basis steeringMat(up, wheel.m_steering);
  353. Basis rotatingMat(right, wheel.m_rotation);
  354. Basis basis2(
  355. right[0], up[0], fwd[0],
  356. right[1], up[1], fwd[1],
  357. right[2], up[2], fwd[2]);
  358. wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2);
  359. //wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
  360. wheel.m_worldTransform.set_origin(origin);
  361. }
  362. real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
  363. VehicleWheel3D &wheel = *wheels[p_idx];
  364. _update_wheel_transform(wheel, s);
  365. real_t depth = -1;
  366. real_t raylen = wheel.m_suspensionRestLength + wheel.m_wheelRadius;
  367. Vector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen);
  368. Vector3 source = wheel.m_raycastInfo.m_hardPointWS;
  369. wheel.m_raycastInfo.m_contactPointWS = source + rayvector;
  370. const Vector3 &target = wheel.m_raycastInfo.m_contactPointWS;
  371. source -= wheel.m_wheelRadius * wheel.m_raycastInfo.m_wheelDirectionWS;
  372. real_t param = real_t(0.);
  373. PhysicsDirectSpaceState3D::RayResult rr;
  374. PhysicsDirectSpaceState3D *ss = s->get_space_state();
  375. PhysicsDirectSpaceState3D::RayParameters ray_params;
  376. ray_params.from = source;
  377. ray_params.to = target;
  378. ray_params.exclude = exclude;
  379. ray_params.collision_mask = get_collision_mask();
  380. wheel.m_raycastInfo.m_groundObject = nullptr;
  381. bool col = ss->intersect_ray(ray_params, rr);
  382. if (col) {
  383. param = source.distance_to(rr.position) / source.distance_to(target);
  384. depth = raylen * param;
  385. wheel.m_raycastInfo.m_contactNormalWS = rr.normal;
  386. wheel.m_raycastInfo.m_isInContact = true;
  387. if (rr.collider) {
  388. wheel.m_raycastInfo.m_groundObject = Object::cast_to<PhysicsBody3D>(rr.collider);
  389. }
  390. real_t hitDistance = param * raylen;
  391. wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius;
  392. //clamp on max suspension travel
  393. real_t minSuspensionLength = wheel.m_suspensionRestLength - wheel.m_maxSuspensionTravel;
  394. real_t maxSuspensionLength = wheel.m_suspensionRestLength + wheel.m_maxSuspensionTravel;
  395. if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) {
  396. wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength;
  397. }
  398. if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) {
  399. wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength;
  400. }
  401. wheel.m_raycastInfo.m_contactPointWS = rr.position;
  402. real_t denominator = wheel.m_raycastInfo.m_contactNormalWS.dot(wheel.m_raycastInfo.m_wheelDirectionWS);
  403. Vector3 chassis_velocity_at_contactPoint;
  404. //Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition();
  405. //chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos);
  406. chassis_velocity_at_contactPoint = s->get_linear_velocity() +
  407. (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin); // * mPos);
  408. real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot(chassis_velocity_at_contactPoint);
  409. if (denominator >= real_t(-0.1)) {
  410. wheel.m_suspensionRelativeVelocity = real_t(0.0);
  411. wheel.m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
  412. } else {
  413. real_t inv = real_t(-1.) / denominator;
  414. wheel.m_suspensionRelativeVelocity = projVel * inv;
  415. wheel.m_clippedInvContactDotSuspension = inv;
  416. }
  417. } else {
  418. wheel.m_raycastInfo.m_isInContact = false;
  419. //put wheel info as in rest position
  420. wheel.m_raycastInfo.m_suspensionLength = wheel.m_suspensionRestLength;
  421. wheel.m_suspensionRelativeVelocity = real_t(0.0);
  422. wheel.m_raycastInfo.m_contactNormalWS = -wheel.m_raycastInfo.m_wheelDirectionWS;
  423. wheel.m_clippedInvContactDotSuspension = real_t(1.0);
  424. }
  425. return depth;
  426. }
  427. void VehicleBody3D::_update_suspension(PhysicsDirectBodyState3D *s) {
  428. real_t chassisMass = get_mass();
  429. for (int w_it = 0; w_it < wheels.size(); w_it++) {
  430. VehicleWheel3D &wheel_info = *wheels[w_it];
  431. if (wheel_info.m_raycastInfo.m_isInContact) {
  432. real_t force;
  433. //Spring
  434. {
  435. real_t susp_length = wheel_info.m_suspensionRestLength;
  436. real_t current_length = wheel_info.m_raycastInfo.m_suspensionLength;
  437. real_t length_diff = (susp_length - current_length);
  438. force = wheel_info.m_suspensionStiffness * length_diff * wheel_info.m_clippedInvContactDotSuspension;
  439. }
  440. // Damper
  441. {
  442. real_t projected_rel_vel = wheel_info.m_suspensionRelativeVelocity;
  443. {
  444. real_t susp_damping;
  445. if (projected_rel_vel < real_t(0.0)) {
  446. susp_damping = wheel_info.m_wheelsDampingCompression;
  447. } else {
  448. susp_damping = wheel_info.m_wheelsDampingRelaxation;
  449. }
  450. force -= susp_damping * projected_rel_vel;
  451. }
  452. }
  453. // RESULT
  454. wheel_info.m_wheelsSuspensionForce = force * chassisMass;
  455. if (wheel_info.m_wheelsSuspensionForce < real_t(0.)) {
  456. wheel_info.m_wheelsSuspensionForce = real_t(0.);
  457. }
  458. } else {
  459. wheel_info.m_wheelsSuspensionForce = real_t(0.0);
  460. }
  461. }
  462. }
  463. //bilateral constraint between two dynamic objects
  464. void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const Vector3 &pos1,
  465. PhysicsBody3D *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence) {
  466. real_t normalLenSqr = normal.length_squared();
  467. //ERR_FAIL_COND( normalLenSqr < real_t(1.1));
  468. if (normalLenSqr > real_t(1.1)) {
  469. impulse = real_t(0.);
  470. return;
  471. }
  472. Vector3 rel_pos1 = pos1 - s->get_transform().origin;
  473. Vector3 rel_pos2;
  474. if (body2) {
  475. rel_pos2 = pos2 - body2->get_global_transform().origin;
  476. }
  477. // This Jacobian entry could be reused for all iterations.
  478. Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1); // * mPos);
  479. Vector3 vel2;
  480. if (body2) {
  481. vel2 = body2->get_linear_velocity() + body2->get_angular_velocity().cross(rel_pos2);
  482. }
  483. Vector3 vel = vel1 - vel2;
  484. Basis b2trans;
  485. real_t b2invmass = 0;
  486. Vector3 b2lv;
  487. Vector3 b2av;
  488. Vector3 b2invinertia; //todo
  489. if (body2) {
  490. b2trans = body2->get_global_transform().basis.transposed();
  491. b2invmass = body2->get_inverse_mass();
  492. b2lv = body2->get_linear_velocity();
  493. b2av = body2->get_angular_velocity();
  494. }
  495. btVehicleJacobianEntry jac(s->get_transform().basis.transposed(),
  496. b2trans,
  497. rel_pos1,
  498. rel_pos2,
  499. normal,
  500. s->get_inverse_inertia_tensor().get_main_diagonal(),
  501. 1.0 / get_mass(),
  502. b2invinertia,
  503. b2invmass);
  504. // FIXME: rel_vel assignment here is overwritten by the following assignment.
  505. // What seems to be intended in the next assignment is: rel_vel = normal.dot(rel_vel);
  506. // Investigate why.
  507. real_t rel_vel = jac.getRelativeVelocity(
  508. s->get_linear_velocity(),
  509. s->get_transform().basis.transposed().xform(s->get_angular_velocity()),
  510. b2lv,
  511. b2trans.xform(b2av));
  512. rel_vel = normal.dot(vel);
  513. // !BAS! We had this set to 0.4, in bullet its 0.2
  514. real_t contactDamping = real_t(0.2);
  515. if (p_rollInfluence > 0.0) {
  516. // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
  517. // keeping in mind our anti roll factor if it is set
  518. contactDamping = MIN(contactDamping, s->get_step() / p_rollInfluence);
  519. }
  520. #define ONLY_USE_LINEAR_MASS
  521. #ifdef ONLY_USE_LINEAR_MASS
  522. real_t massTerm = real_t(1.) / ((1.0 / get_mass()) + b2invmass);
  523. impulse = -contactDamping * rel_vel * massTerm;
  524. #else
  525. real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv;
  526. impulse = velocityImpulse;
  527. #endif
  528. }
  529. VehicleBody3D::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState3D *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse) :
  530. m_s(s),
  531. m_body1(body1),
  532. m_frictionPositionWorld(frictionPosWorld),
  533. m_frictionDirectionWorld(frictionDirectionWorld),
  534. m_maxImpulse(maxImpulse) {
  535. real_t denom0 = 0;
  536. real_t denom1 = 0;
  537. {
  538. Vector3 r0 = frictionPosWorld - s->get_transform().origin;
  539. Vector3 c0 = (r0).cross(frictionDirectionWorld);
  540. Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
  541. denom0 = s->get_inverse_mass() + frictionDirectionWorld.dot(vec);
  542. }
  543. /* TODO: Why is this code unused?
  544. if (body1) {
  545. Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin;
  546. Vector3 c0 = (r0).cross(frictionDirectionWorld);
  547. Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
  548. //denom1= body1->get_inverse_mass() + frictionDirectionWorld.dot(vec);
  549. }
  550. */
  551. real_t relaxation = 1.f;
  552. m_jacDiagABInv = relaxation / (denom0 + denom1);
  553. }
  554. real_t VehicleBody3D::_calc_rolling_friction(btVehicleWheelContactPoint &contactPoint) {
  555. real_t j1 = 0.f;
  556. const Vector3 &contactPosWorld = contactPoint.m_frictionPositionWorld;
  557. Vector3 rel_pos1 = contactPosWorld - contactPoint.m_s->get_transform().origin;
  558. Vector3 rel_pos2;
  559. if (contactPoint.m_body1) {
  560. rel_pos2 = contactPosWorld - contactPoint.m_body1->get_global_transform().origin;
  561. }
  562. real_t maxImpulse = contactPoint.m_maxImpulse;
  563. Vector3 vel1 = contactPoint.m_s->get_linear_velocity() + (contactPoint.m_s->get_angular_velocity()).cross(rel_pos1); // * mPos);
  564. Vector3 vel2;
  565. if (contactPoint.m_body1) {
  566. vel2 = contactPoint.m_body1->get_linear_velocity() + contactPoint.m_body1->get_angular_velocity().cross(rel_pos2);
  567. }
  568. Vector3 vel = vel1 - vel2;
  569. real_t vrel = contactPoint.m_frictionDirectionWorld.dot(vel);
  570. // calculate j that moves us to zero relative velocity
  571. j1 = -vrel * contactPoint.m_jacDiagABInv;
  572. return CLAMP(j1, -maxImpulse, maxImpulse);
  573. }
  574. static const real_t sideFrictionStiffness2 = real_t(1.0);
  575. void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) {
  576. //calculate the impulse, so that the wheels don't move sidewards
  577. int numWheel = wheels.size();
  578. if (!numWheel) {
  579. return;
  580. }
  581. m_forwardWS.resize(numWheel);
  582. m_axle.resize(numWheel);
  583. m_forwardImpulse.resize(numWheel);
  584. m_sideImpulse.resize(numWheel);
  585. //collapse all those loops into one!
  586. for (int i = 0; i < wheels.size(); i++) {
  587. m_sideImpulse.write[i] = real_t(0.);
  588. m_forwardImpulse.write[i] = real_t(0.);
  589. }
  590. {
  591. for (int i = 0; i < wheels.size(); i++) {
  592. VehicleWheel3D &wheelInfo = *wheels[i];
  593. if (wheelInfo.m_raycastInfo.m_isInContact) {
  594. //const btTransform& wheelTrans = getWheelTransformWS( i );
  595. Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis;
  596. m_axle.write[i] = wheelBasis0.get_column(Vector3::AXIS_X);
  597. //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS;
  598. const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS;
  599. real_t proj = m_axle[i].dot(surfNormalWS);
  600. m_axle.write[i] -= surfNormalWS * proj;
  601. m_axle.write[i] = m_axle[i].normalized();
  602. m_forwardWS.write[i] = surfNormalWS.cross(m_axle[i]);
  603. m_forwardWS.write[i].normalize();
  604. _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
  605. wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
  606. m_axle[i], m_sideImpulse.write[i], wheelInfo.m_rollInfluence);
  607. m_sideImpulse.write[i] *= sideFrictionStiffness2;
  608. }
  609. }
  610. }
  611. real_t sideFactor = real_t(1.);
  612. real_t fwdFactor = 0.5;
  613. bool sliding = false;
  614. {
  615. for (int wheel = 0; wheel < wheels.size(); wheel++) {
  616. VehicleWheel3D &wheelInfo = *wheels[wheel];
  617. //class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject;
  618. real_t rollingFriction = 0.f;
  619. if (wheelInfo.m_raycastInfo.m_isInContact) {
  620. if (wheelInfo.m_engineForce != 0.f) {
  621. rollingFriction = -wheelInfo.m_engineForce * s->get_step();
  622. } else {
  623. real_t defaultRollingFrictionImpulse = 0.f;
  624. real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse;
  625. btVehicleWheelContactPoint contactPt(s, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, m_forwardWS[wheel], maxImpulse);
  626. rollingFriction = _calc_rolling_friction(contactPt);
  627. }
  628. }
  629. //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break)
  630. m_forwardImpulse.write[wheel] = real_t(0.);
  631. wheelInfo.m_skidInfo = real_t(1.);
  632. if (wheelInfo.m_raycastInfo.m_isInContact) {
  633. wheelInfo.m_skidInfo = real_t(1.);
  634. real_t maximp = wheelInfo.m_wheelsSuspensionForce * s->get_step() * wheelInfo.m_frictionSlip;
  635. real_t maximpSide = maximp;
  636. real_t maximpSquared = maximp * maximpSide;
  637. m_forwardImpulse.write[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep;
  638. real_t x = (m_forwardImpulse[wheel]) * fwdFactor;
  639. real_t y = (m_sideImpulse[wheel]) * sideFactor;
  640. real_t impulseSquared = (x * x + y * y);
  641. if (impulseSquared > maximpSquared) {
  642. sliding = true;
  643. real_t factor = maximp / Math::sqrt(impulseSquared);
  644. wheelInfo.m_skidInfo *= factor;
  645. }
  646. }
  647. }
  648. }
  649. if (sliding) {
  650. for (int wheel = 0; wheel < wheels.size(); wheel++) {
  651. if (m_sideImpulse[wheel] != real_t(0.)) {
  652. if (wheels[wheel]->m_skidInfo < real_t(1.)) {
  653. m_forwardImpulse.write[wheel] *= wheels[wheel]->m_skidInfo;
  654. m_sideImpulse.write[wheel] *= wheels[wheel]->m_skidInfo;
  655. }
  656. }
  657. }
  658. }
  659. // apply the impulses
  660. {
  661. for (int wheel = 0; wheel < wheels.size(); wheel++) {
  662. VehicleWheel3D &wheelInfo = *wheels[wheel];
  663. Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS -
  664. s->get_transform().origin;
  665. if (m_forwardImpulse[wheel] != real_t(0.)) {
  666. s->apply_impulse(m_forwardWS[wheel] * (m_forwardImpulse[wheel]), rel_pos);
  667. }
  668. if (m_sideImpulse[wheel] != real_t(0.)) {
  669. PhysicsBody3D *groundObject = wheelInfo.m_raycastInfo.m_groundObject;
  670. Vector3 rel_pos2;
  671. if (groundObject) {
  672. rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - groundObject->get_global_transform().origin;
  673. }
  674. Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel];
  675. #if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT.
  676. Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1]; //getRigidBody()->getCenterOfMassTransform3D().getBasis().getColumn(m_indexUpAxis);
  677. rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f - wheelInfo.m_rollInfluence));
  678. #else
  679. rel_pos[1] *= wheelInfo.m_rollInfluence; //?
  680. #endif
  681. s->apply_impulse(sideImp, rel_pos);
  682. //apply friction impulse on the ground
  683. //todo
  684. //groundObject->applyImpulse(-sideImp,rel_pos2);
  685. }
  686. }
  687. }
  688. }
  689. void VehicleBody3D::_physics_interpolated_changed() {
  690. _update_process_mode();
  691. RigidBody3D::_physics_interpolated_changed();
  692. }
  693. void VehicleBody3D::fti_pump_xform() {
  694. for (int i = 0; i < wheels.size(); i++) {
  695. VehicleWheel3D &w = *wheels[i];
  696. w.fti_data.pump();
  697. }
  698. RigidBody3D::fti_pump_xform();
  699. }
  700. void VehicleBody3D::_update_process_mode() {
  701. set_process_internal(is_physics_interpolated_and_enabled());
  702. }
  703. void VehicleBody3D::_notification(int p_what) {
  704. switch (p_what) {
  705. case NOTIFICATION_ENTER_TREE: {
  706. _update_process_mode();
  707. } break;
  708. case NOTIFICATION_INTERNAL_PROCESS: {
  709. #ifdef DEV_ENABLED
  710. if (!is_physics_interpolated_and_enabled()) {
  711. WARN_PRINT_ONCE("VehicleBody NOTIFICATION_INTERNAL_PROCESS with physics interpolation OFF. (benign)");
  712. }
  713. #endif
  714. real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
  715. Transform3D xform;
  716. Transform3D inv_vehicle_xform = get_global_transform_interpolated().affine_inverse();
  717. for (int i = 0; i < wheels.size(); i++) {
  718. VehicleWheel3D &w = *wheels[i];
  719. w.fti_data.update_world_xform(xform, f);
  720. w.set_transform(inv_vehicle_xform * xform);
  721. }
  722. } break;
  723. case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
  724. _update_process_mode();
  725. for (int i = 0; i < wheels.size(); i++) {
  726. VehicleWheel3D &w = *wheels[i];
  727. w.fti_data.reset_queued = true;
  728. }
  729. } break;
  730. default:
  731. break;
  732. }
  733. }
  734. void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
  735. RigidBody3D::_body_state_changed(p_state);
  736. real_t step = p_state->get_step();
  737. for (int i = 0; i < wheels.size(); i++) {
  738. _update_wheel(i, p_state);
  739. }
  740. bool use_fti = is_physics_interpolated_and_enabled();
  741. for (int i = 0; i < wheels.size(); i++) {
  742. _ray_cast(i, p_state);
  743. if (!use_fti) {
  744. // TODO: can this path also just use world space directly instead of inverse parent space?
  745. wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform);
  746. }
  747. }
  748. _update_suspension(p_state);
  749. for (int i = 0; i < wheels.size(); i++) {
  750. //apply suspension force
  751. VehicleWheel3D &wheel = *wheels[i];
  752. real_t suspensionForce = wheel.m_wheelsSuspensionForce;
  753. if (suspensionForce > wheel.m_maxSuspensionForce) {
  754. suspensionForce = wheel.m_maxSuspensionForce;
  755. }
  756. Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step;
  757. Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - p_state->get_transform().origin;
  758. p_state->apply_impulse(impulse, relative_position);
  759. }
  760. _update_friction(p_state);
  761. for (int i = 0; i < wheels.size(); i++) {
  762. VehicleWheel3D &wheel = *wheels[i];
  763. Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - p_state->get_transform().origin;
  764. Vector3 vel = p_state->get_linear_velocity() + (p_state->get_angular_velocity()).cross(relpos);
  765. if (wheel.m_raycastInfo.m_isInContact) {
  766. const Transform3D &chassisWorldTransform = p_state->get_transform();
  767. // Get forward vector.
  768. Vector3 fwd(
  769. chassisWorldTransform.basis[0][Vector3::AXIS_Z],
  770. chassisWorldTransform.basis[1][Vector3::AXIS_Z],
  771. chassisWorldTransform.basis[2][Vector3::AXIS_Z]);
  772. // Apply steering rotation to forward vector for steerable wheels.
  773. if (wheel.steers) {
  774. Basis steering_mat(Vector3(0, 1, 0), wheel.m_steering);
  775. fwd = steering_mat.xform(fwd);
  776. }
  777. real_t proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS);
  778. fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj;
  779. fwd.normalize();
  780. real_t proj2 = fwd.dot(vel);
  781. wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius);
  782. }
  783. wheel.m_rotation += wheel.m_deltaRotation;
  784. if (use_fti) {
  785. wheel.fti_data.rotate(wheel.m_deltaRotation);
  786. }
  787. wheel.m_rpm = ((wheel.m_deltaRotation / step) * 60) / Math::TAU;
  788. wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact
  789. }
  790. }
  791. void VehicleBody3D::set_engine_force(real_t p_engine_force) {
  792. engine_force = p_engine_force;
  793. for (int i = 0; i < wheels.size(); i++) {
  794. VehicleWheel3D &wheelInfo = *wheels[i];
  795. if (wheelInfo.engine_traction) {
  796. wheelInfo.m_engineForce = p_engine_force;
  797. }
  798. }
  799. }
  800. real_t VehicleBody3D::get_engine_force() const {
  801. return engine_force;
  802. }
  803. void VehicleBody3D::set_brake(real_t p_brake) {
  804. brake = p_brake;
  805. for (int i = 0; i < wheels.size(); i++) {
  806. VehicleWheel3D &wheelInfo = *wheels[i];
  807. wheelInfo.m_brake = p_brake;
  808. }
  809. }
  810. real_t VehicleBody3D::get_brake() const {
  811. return brake;
  812. }
  813. void VehicleBody3D::set_steering(real_t p_steering) {
  814. m_steeringValue = p_steering;
  815. for (int i = 0; i < wheels.size(); i++) {
  816. VehicleWheel3D &wheelInfo = *wheels[i];
  817. if (wheelInfo.steers) {
  818. wheelInfo.m_steering = p_steering;
  819. }
  820. }
  821. }
  822. real_t VehicleBody3D::get_steering() const {
  823. return m_steeringValue;
  824. }
  825. void VehicleBody3D::_bind_methods() {
  826. ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleBody3D::set_engine_force);
  827. ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleBody3D::get_engine_force);
  828. ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleBody3D::set_brake);
  829. ClassDB::bind_method(D_METHOD("get_brake"), &VehicleBody3D::get_brake);
  830. ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleBody3D::set_steering);
  831. ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering);
  832. ADD_GROUP("Motion", "");
  833. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, U"-1024,1024,0.01,or_less,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_engine_force", "get_engine_force");
  834. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, U"-128,128,0.01,or_less,or_greater,suffix:kg\u22C5m/s\u00B2 (N)"), "set_brake", "get_brake");
  835. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180,0.01,radians_as_degrees"), "set_steering", "get_steering");
  836. }
  837. VehicleBody3D::VehicleBody3D() {
  838. exclude.insert(get_rid());
  839. set_mass(40);
  840. }