wheel.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWPhys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/wheel.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Byon_g $*
  29. * *
  30. * $Modtime:: 3/19/02 2:32p $*
  31. * *
  32. * $Revision:: 27 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * SuspensionElementClass::SuspensionElementClass -- Constructor *
  37. * SuspensionElementClass::~SuspensionElementClass -- Destructor *
  38. * SuspensionElementClass::Init -- Initialization, pass in bone indices for this wheel *
  39. * SuspensionElementClass::Intersect_Spring -- Intersect the suspension spring with the terr *
  40. * SuspensionElementClass::Update_Model -- Update the wheel transforms in the model. *
  41. * SuspensionElementClass::Translate_Wheel_On_Axis -- Translates the wheel to the contact po *
  42. * SuspensionElementClass::Translate_Wheel -- Translates the wheel to the contact point *
  43. * SuspensionElementClass::Rotate_Fork -- Rotates the "fork" until the wheel touches *
  44. * WheelClass::WheelClass -- Constructor *
  45. * WheelClass::~WheelClass -- Destructor *
  46. * WheelClass::Init -- Initialize the wheel object *
  47. * WheelClass::Compute_Force_And_Torque -- Computes wheel forces *
  48. * WheelClass::Compute_Suspension_Force -- Computes the suspension force *
  49. * WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity -- Computes the current angular veloci *
  50. * WheelClass::Apply_Forces -- Computes the final forces and adds them *
  51. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52. #include "wheel.h"
  53. #include "wheelvehicle.h"
  54. #include "trackedvehicle.h"
  55. #include "lineseg.h"
  56. #include "physcoltest.h"
  57. #include "physcon.h"
  58. #include "pscene.h"
  59. #include "wwprofile.h"
  60. // Wheel friction is a proportional controller, this is the constant.
  61. // TODO: Should we make this an editable parameter of VehiclePhysDefClass?
  62. const float WHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 7.5f;
  63. const float BIKE_WHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 3.0f;
  64. const float TRACKWHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 5.0f;
  65. const float VTOLWHEEL_FRICTION_PENALTY_CONSTANT = 1.0f / 3.0f;
  66. // Debugging
  67. const Vector3 SUSPENSION_FORCE_COLOR = Vector3(0,0,1);
  68. const Vector3 LATERAL_FORCE_COLOR = Vector3(0,1,0);
  69. const Vector3 TRACTIVE_FORCE_COLOR = Vector3(1,0,0);
  70. // Maximum total acceleration the suspension will exert on the parent vehicle (in m/s^2)
  71. // Note, each "real" wheel gets an equal fraction of this, so if you have 4 wheels, the max
  72. // acceleration any single wheel will exert is one quarter of the total.
  73. const float MAX_SUSPENSION_ACCEL = 50.0f;
  74. /*
  75. ** Wheels: An instance of this object will contain information on each
  76. ** wheel detected in the vehicle model. Wheels use a pair of named bones.
  77. ** The "position" bone is used to move the wheel up and down according to
  78. ** the suspension system and to rotate if this is a "steering" wheel. The
  79. ** "rotation" bone is used to roll the wheel on the terrain.
  80. **
  81. ** More Info on the "Position" and "Center" bones:
  82. ** - The "center" bone is only used in computing the radius of the wheel and
  83. ** it is rotated about its z-axis to make the wheel appear to roll
  84. ** - The "position" bone is used in conjunction with the length parameter
  85. ** to define the suspension springs.
  86. **
  87. ** Defining the "spring-segment" using the position bone:
  88. ** - The default location of the position bone is considered the point of maximum-
  89. ** compression. (this point should be *inside* the collision box for the model!)
  90. ** - The spring will extend down the -z axis in the position bone's coordinate system
  91. **
  92. ** Graphical constraints for the wheel
  93. ** - One of three methods can be used to graphically constrain the wheel with the
  94. ** ground: Translation along the z-axis of the position bone, or rotation of
  95. ** a rotation constraint bone.
  96. ** - Normal case: There are two bones for the wheel: position and center. The position
  97. ** bone is moved along its Z-axis to the point of collision with the ground
  98. ** - Translation: There is an additional translation bone (WheelTxx) which defines
  99. ** the axis that the wheel is to be translated along.
  100. ** - Fork/Rotation: a "fork" bone (WheelFxx) is rotated such that the Z-coordinate (in
  101. ** the fork's coordinate system) meets the ground.
  102. **
  103. ** Wheel Flags:
  104. ** - 'E' Engine. this wheel is connected to the engine and should exert its force
  105. ** - 'S' Steering: The position bone for this wheel rotates about its Z-axis for steering
  106. ** - 'L' Left Track: this wheel is part of the left track of a tracked vehicle
  107. ** - 'R' Right Track: this wheel is part of the right track of a tracked vehicle
  108. */
  109. /*********************************************************************************************************
  110. SuspensionElementClass Implementation
  111. *********************************************************************************************************/
  112. /***********************************************************************************************
  113. * SuspensionElementClass::SuspensionElementClass -- Constructor *
  114. * *
  115. * INPUT: *
  116. * *
  117. * OUTPUT: *
  118. * *
  119. * WARNINGS: *
  120. * *
  121. * HISTORY: *
  122. *=============================================================================================*/
  123. SuspensionElementClass::SuspensionElementClass(void):
  124. Parent(NULL),
  125. Flags(DEFAULT_FLAGS),
  126. PositionBone(-1),
  127. AxisBone(-1),
  128. ForkBone(-1),
  129. ObjWheelTM(1),
  130. SpringConstant(DEFAULT_SPRING_CONSTANT),
  131. DampingCoefficient(DEFAULT_DAMPING_COEFFICIENT),
  132. SpringLength(DEFAULT_SPRING_LENGTH),
  133. SteeringAngle(0.0f),
  134. SuspensionForce(0,0,0),
  135. WheelTM(1),
  136. WheelP0(0,0,0),
  137. Contact(0,0,0),
  138. Normal(0,0,0),
  139. ContactSurface(0),
  140. LastPoint(0,0,0),
  141. TranslationScale(1.0f),
  142. ObjAxis(0,0,-1),
  143. ObjForkTM(1),
  144. ForkLength(0.0f),
  145. ForkZ(0.0f),
  146. ForkSin0(0.0f),
  147. ForkCos0(0.0f),
  148. ForkA(0.0f),
  149. ForkB(0.0f),
  150. SpringEndP0(0.0f,0.0f,0.0f),
  151. SpringEndP1(0.0f,0.0f,0.0f)
  152. {
  153. }
  154. /***********************************************************************************************
  155. * SuspensionElementClass::~SuspensionElementClass -- Destructor *
  156. * *
  157. * INPUT: *
  158. * *
  159. * OUTPUT: *
  160. * *
  161. * WARNINGS: *
  162. * *
  163. * HISTORY: *
  164. * 12/18/2000 gth : Created. *
  165. *=============================================================================================*/
  166. SuspensionElementClass::~SuspensionElementClass(void)
  167. {
  168. if (Parent != NULL) {
  169. RenderObjClass * model = Parent->Peek_Model();
  170. if (model != NULL) {
  171. if (PositionBone != -1) {
  172. model->Release_Bone(PositionBone);
  173. }
  174. if (ForkBone != -1) {
  175. model->Release_Bone(ForkBone);
  176. }
  177. if (AxisBone != -1) {
  178. model->Release_Bone(AxisBone);
  179. }
  180. }
  181. }
  182. }
  183. /***********************************************************************************************
  184. * SuspensionElementClass::Init -- Initialization, pass in bone indices for this wheel *
  185. * *
  186. * INPUT: *
  187. * obj - parent vehicle physics object *
  188. * pbone - index of the position bone (position bone is the contact point for the wheel) *
  189. * rbone - index of the center bone (center of rotation) *
  190. * fbone - index of the fork bone for a "fork" constraint (bad name, this is the back wheel of *
  191. * a motorcycle) *
  192. * abone - index of the bone for an axis constraint. (like the front wheel of a motorcycle) *
  193. * *
  194. * OUTPUT: *
  195. * *
  196. * WARNINGS: *
  197. * *
  198. * HISTORY: *
  199. * 12/18/2000 gth : Created. *
  200. *=============================================================================================*/
  201. void SuspensionElementClass::Init(VehiclePhysClass * obj,int pbone,int rbone,int fbone,int abone)
  202. {
  203. WWASSERT(obj != NULL); // have to have a valid object
  204. WWASSERT(pbone != -1); // have to have a position bone at least
  205. WWASSERT(obj->Peek_Model() != NULL); // have to have a valid model
  206. // Store a pointer to the vehicle. Not referenced because this class meant to be a private
  207. // member of wheeled vehicle. A reference here would be circular.
  208. Parent = obj;
  209. // Grab a pointer to the render model
  210. RenderObjClass * model = obj->Peek_Model();
  211. // Store the bone indices and capture the bones
  212. PositionBone = pbone;
  213. ForkBone = fbone;
  214. AxisBone = abone;
  215. WWASSERT(PositionBone != -1);
  216. model->Capture_Bone(PositionBone);
  217. if (ForkBone != -1) {
  218. model->Capture_Bone(ForkBone);
  219. }
  220. if (AxisBone != -1) {
  221. model->Capture_Bone(AxisBone);
  222. }
  223. // Compute the object-space transform of the wheel
  224. Matrix3D position_bone,rootinv;
  225. const Matrix3D & root = model->Get_Transform();
  226. root.Get_Orthogonal_Inverse(rootinv);
  227. position_bone = model->Get_Bone_Transform(PositionBone);
  228. Matrix3D::Multiply(rootinv,position_bone,&ObjWheelTM);
  229. WheelTM = position_bone;
  230. WheelTM.Get_Translation(&WheelP0);
  231. // Initialize the last position of the wheel
  232. LastPoint = position_bone.Get_Translation();
  233. // If a fork is constraint being used, compute the constants for it
  234. if (ForkBone != -1) {
  235. Matrix3D::Multiply(rootinv,model->Get_Bone_Transform(ForkBone),&ObjForkTM);
  236. Vector3 r = ObjWheelTM.Get_Translation() - ObjForkTM.Get_Translation();
  237. ForkLength = r.Length();
  238. Vector3 pos;
  239. ObjWheelTM.Get_Translation(&pos);
  240. Matrix3D::Inverse_Transform_Vector(ObjForkTM,pos,&pos);
  241. WWASSERT(WWMath::Fabs(ForkLength - pos.Length()) < 0.1f);
  242. ForkZ = pos.Z;
  243. ForkSin0 = pos.Z / ForkLength;
  244. ForkCos0 = pos.X / ForkLength;
  245. ForkA = ForkCos0 / ForkSin0;
  246. ForkB = -(ForkCos0 * ForkCos0) / ForkSin0 - ForkSin0;
  247. }
  248. // If a translation axis constraint is being used, compute the axis
  249. if (AxisBone != -1) {
  250. Matrix3D axistm;
  251. Matrix3D::Multiply(rootinv,model->Get_Bone_Transform(AxisBone),&axistm);
  252. axistm.Get_Z_Vector(&ObjAxis);
  253. Vector3 springdir;
  254. position_bone.Get_Z_Vector(&springdir);
  255. TranslationScale = 1.0f / Vector3::Dot_Product(ObjAxis,springdir);
  256. }
  257. }
  258. /***********************************************************************************************
  259. * SuspensionElementClass::Intersect_Spring -- Intersect the suspension spring with the terrai *
  260. * *
  261. * INPUT: *
  262. * *
  263. * OUTPUT: *
  264. * *
  265. * WARNINGS: *
  266. * *
  267. * HISTORY: *
  268. * 12/18/2000 gth : Created. *
  269. *=============================================================================================*/
  270. void SuspensionElementClass::Intersect_Spring(void)
  271. {
  272. WWASSERT(Parent != NULL);
  273. // transform the wheel coordinate system into world space.
  274. Matrix3D::Multiply(Parent->Get_Transform(),ObjWheelTM,&WheelTM);
  275. // compute WheelP0 and temporary p1 (endpoints of the spring)
  276. Vector3 p1;
  277. WheelTM.Get_Translation(&WheelP0);
  278. Matrix3D::Transform_Vector(WheelTM,Vector3(0,0,-SpringLength),&p1);
  279. // If the spring endpoints haven't changed don't do raycast (very expensive)
  280. // TODO: Ensure we are not on top of an animated mesh!
  281. if (p1==SpringEndP1 && WheelP0==SpringEndP0) return;
  282. SpringEndP1=p1;
  283. SpringEndP0=WheelP0;
  284. // cast a ray, colliding with "physical" objects
  285. LineSegClass line(WheelP0,p1);
  286. CastResultStruct result;
  287. PhysRayCollisionTestClass raytest(line,&result,Parent->Get_Collision_Group(),COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VEHICLE);
  288. PhysicsSceneClass::Get_Instance()->Cast_Ray(raytest,false);
  289. // evaluate the result of the raycast
  290. if (result.Fraction >= 1.0f) {
  291. Set_Flag(INCONTACT,false);
  292. WheelTM.Set_Translation(p1);
  293. Contact = p1;
  294. } else {
  295. Set_Flag(INCONTACT,true);
  296. line.Compute_Point(result.Fraction,&Contact);
  297. WheelTM.Set_Translation(Contact);
  298. Normal = result.Normal;
  299. ContactSurface = result.SurfaceType;
  300. // TODO: Low-level collision routines should handle this?
  301. if (Vector3::Dot_Product(Normal,line.Get_Dir()) > 0.0f) {
  302. Normal = -Normal;
  303. }
  304. }
  305. }
  306. /***********************************************************************************************
  307. * SuspensionElementClass::Non_Physical_Intersect_Spring -- used in ecyclopedia viewer... *
  308. * *
  309. * INPUT: *
  310. * *
  311. * OUTPUT: *
  312. * *
  313. * WARNINGS: *
  314. * *
  315. * HISTORY: *
  316. *=============================================================================================*/
  317. void SuspensionElementClass::Non_Physical_Intersect_Spring(float suspension_fraction)
  318. {
  319. WWASSERT(Parent != NULL);
  320. // transform the wheel coordinate system into world space.
  321. Matrix3D::Multiply(Parent->Get_Transform(),ObjWheelTM,&WheelTM);
  322. // compute WheelP0 and temporary p1 (endpoints of the spring)
  323. Vector3 p1;
  324. WheelTM.Get_Translation(&WheelP0);
  325. Matrix3D::Transform_Vector(WheelTM,Vector3(0,0,-SpringLength),&p1);
  326. // Form a ray to compute the contact point.
  327. Set_Flag(INCONTACT,true);
  328. LineSegClass line(WheelP0,p1);
  329. line.Compute_Point(suspension_fraction,&Contact);
  330. WheelTM.Set_Translation(Contact);
  331. Normal.Set(0.0f,0.0f,1.0f);
  332. ContactSurface = 0;
  333. }
  334. /***********************************************************************************************
  335. * SuspensionElementClass::Update_Model -- Update the wheel transforms in the model. *
  336. * *
  337. * INPUT: *
  338. * *
  339. * OUTPUT: *
  340. * *
  341. * WARNINGS: *
  342. * *
  343. * HISTORY: *
  344. * 12/18/2000 gth : Created. *
  345. *=============================================================================================*/
  346. void SuspensionElementClass::Update_Model(void)
  347. {
  348. RenderObjClass * model = Parent->Peek_Model();
  349. if (model == NULL) return;
  350. // update the contact point.
  351. Intersect_Spring();
  352. // use the constraints to make the wheel touch the ground
  353. if (ForkBone != -1) {
  354. Rotate_Fork(model);
  355. } else if (AxisBone != -1) {
  356. Translate_Wheel_On_Axis(model);
  357. } else {
  358. Translate_Wheel(model);
  359. }
  360. }
  361. /***********************************************************************************************
  362. * SuspensionElementClass::Non_Physical_Update -- Update the wheel transforms in the model. *
  363. * *
  364. * INPUT: *
  365. * *
  366. * OUTPUT: *
  367. * *
  368. * WARNINGS: *
  369. * *
  370. * HISTORY: *
  371. * 12/18/2000 gth : Created. *
  372. *=============================================================================================*/
  373. void SuspensionElementClass::Non_Physical_Update(float suspension_fraction,float rotation)
  374. {
  375. RenderObjClass * model = Parent->Peek_Model();
  376. if (model == NULL) return;
  377. // Set the wheel up as if it had an intersection at 'fraction' of its length
  378. Non_Physical_Intersect_Spring(suspension_fraction);
  379. // Update the "constraints"
  380. if (ForkBone != -1) {
  381. Rotate_Fork(model);
  382. } else if (AxisBone != -1) {
  383. Translate_Wheel_On_Axis(model);
  384. } else {
  385. Translate_Wheel(model);
  386. }
  387. }
  388. /***********************************************************************************************
  389. * SuspensionElementClass::Translate_Wheel_On_Axis -- Translates the wheel to the contact poin *
  390. * *
  391. * INPUT: *
  392. * *
  393. * OUTPUT: *
  394. * *
  395. * WARNINGS: *
  396. * *
  397. * HISTORY: *
  398. * 12/18/2000 gth : Created. *
  399. *=============================================================================================*/
  400. void SuspensionElementClass::Translate_Wheel_On_Axis(RenderObjClass * model)
  401. {
  402. // This function handles moving the wheel down to touch the ground
  403. // when there is a translation bone. We just translate down its -z axis
  404. Matrix3D position_tm(1);
  405. position_tm.Rotate_Z(SteeringAngle);
  406. if (Get_Flag(INCONTACT)) {
  407. Vector3 dp;
  408. Vector3 springdir;
  409. Vector3::Subtract(Contact,WheelP0,&dp);
  410. WheelTM.Get_Z_Vector(&springdir);
  411. position_tm.Adjust_Z_Translation(TranslationScale * Vector3::Dot_Product(dp,springdir));
  412. } else {
  413. position_tm.Adjust_Z_Translation(TranslationScale * -SpringLength);
  414. }
  415. model->Control_Bone(AxisBone,position_tm);
  416. }
  417. /***********************************************************************************************
  418. * SuspensionElementClass::Translate_Wheel -- Translates the wheel to the contact point *
  419. * *
  420. * INPUT: *
  421. * *
  422. * OUTPUT: *
  423. * *
  424. * WARNINGS: *
  425. * *
  426. * HISTORY: *
  427. * 12/18/2000 gth : Created. *
  428. *=============================================================================================*/
  429. void SuspensionElementClass::Translate_Wheel(RenderObjClass * model)
  430. {
  431. // This function handles moving the wheel down to touch the ground
  432. // when there is no "fork" present. We just translate down the -z axis
  433. Matrix3D position_tm(1);
  434. position_tm.Rotate_Z(SteeringAngle);
  435. if (Get_Flag(INCONTACT)) {
  436. Vector3 dp;
  437. Vector3 springdir;
  438. Vector3::Subtract(Contact,WheelP0,&dp);
  439. WheelTM.Get_Z_Vector(&springdir);
  440. position_tm.Adjust_Z_Translation(Vector3::Dot_Product(dp,springdir));
  441. } else {
  442. position_tm.Adjust_Z_Translation(-SpringLength);
  443. }
  444. model->Control_Bone(PositionBone,position_tm);
  445. }
  446. /***********************************************************************************************
  447. * SuspensionElementClass::Rotate_Fork -- Rotates the "fork" until the wheel touches *
  448. * *
  449. * Again, "fork" was the wrong name for this. This is a "constraint" like the back wheel *
  450. * of a motorcycle. The "fork" bone will be rotated about its Z axis until the WheelP *
  451. * bone is at the Z-position of the contact point. *
  452. * *
  453. * INPUT: *
  454. * *
  455. * OUTPUT: *
  456. * *
  457. * WARNINGS: *
  458. * *
  459. * HISTORY: *
  460. * 12/18/2000 gth : Created. *
  461. *=============================================================================================*/
  462. void SuspensionElementClass::Rotate_Fork(RenderObjClass * model)
  463. {
  464. WWASSERT(ForkBone != -1);
  465. float fork_sin = 0.0f;
  466. float fork_cos = 1.0f;
  467. // transform the contact point into the fork coordinate system
  468. // - first, transform into object space
  469. // - then transform into "fork" space
  470. Vector3 p1;
  471. Matrix3D::Inverse_Transform_Vector(Parent->Get_Transform(),Contact,&p1);
  472. Matrix3D::Inverse_Transform_Vector(ObjForkTM,p1,&p1);
  473. // Only concerned with the x and z coordinates now. And x is a function of z
  474. float x2 = ForkLength*ForkLength - p1.Z * p1.Z;
  475. if (x2 < 0.0f) {
  476. return;
  477. }
  478. p1.X = WWMath::Sqrt(x2);
  479. // Compute the sine and cosine of the new rotation (without calling atan,sine or cosine!)
  480. float ooforklen = 1.0f / ForkLength;
  481. fork_sin = -(p1.X * ooforklen - ForkA * p1.Z * ooforklen) / ForkB;
  482. fork_cos = (p1.Z * ooforklen) / ForkSin0 + ForkA * fork_sin;
  483. #if 0
  484. float angle1 = atan2(p1.Z,p1.X);
  485. float angledelta = angle1 - atan2(ForkSin0,ForkCos0);
  486. fork_sin = -WWMath::Sin(angledelta);
  487. fork_cos = WWMath::Cos(angledelta);
  488. #endif
  489. Matrix3D fork_rotation(1);
  490. fork_rotation.Rotate_Y(fork_sin,fork_cos);
  491. model->Control_Bone(ForkBone,fork_rotation);
  492. }
  493. /*********************************************************************************************************
  494. WheelClass Implementation
  495. *********************************************************************************************************/
  496. /***********************************************************************************************
  497. * WheelClass::WheelClass -- Constructor *
  498. * *
  499. * INPUT: *
  500. * *
  501. * OUTPUT: *
  502. * *
  503. * WARNINGS: *
  504. * *
  505. * HISTORY: *
  506. *=============================================================================================*/
  507. WheelClass::WheelClass(void) :
  508. Radius(1.0f),
  509. RotationBone(-1),
  510. Rotation(0.0f),
  511. RotationDelta(0.0f),
  512. AxleTorque(0.0f),
  513. TractiveFrictionForce(0,0,0),
  514. LateralFrictionForce(0,0,0),
  515. SlipFactor(1.0f),
  516. IdealAngularVelocity(0.0f)
  517. {
  518. }
  519. /***********************************************************************************************
  520. * WheelClass::~WheelClass -- Destructor *
  521. * *
  522. * INPUT: *
  523. * *
  524. * OUTPUT: *
  525. * *
  526. * WARNINGS: *
  527. * *
  528. * HISTORY: *
  529. * 12/18/2000 gth : Created. *
  530. *=============================================================================================*/
  531. WheelClass::~WheelClass(void)
  532. {
  533. if (Parent != NULL) {
  534. RenderObjClass * model = Parent->Peek_Model();
  535. if (model != NULL) {
  536. if (RotationBone != -1) {
  537. model->Release_Bone(RotationBone);
  538. }
  539. }
  540. }
  541. }
  542. /***********************************************************************************************
  543. * WheelClass::Init -- Initialize the wheel object *
  544. * *
  545. * INPUT: *
  546. * obj - parent vehicle physics object *
  547. * position_bone - WHEELP, contact point, wheel position *
  548. * rotation_bone - WHEELC, center of rotation for the wheel *
  549. * fork_bone - bone for a constraint like the back wheel of a motorcycle (rotates about Z) *
  550. * axis_bone - bone for a constraint like the front wheel of a motorcycle (translates along Z) *
  551. * *
  552. * OUTPUT: *
  553. * *
  554. * WARNINGS: *
  555. * *
  556. * HISTORY: *
  557. * 12/18/2000 gth : Created. *
  558. *=============================================================================================*/
  559. void WheelClass::Init
  560. (
  561. VehiclePhysClass * obj,
  562. int position_bone,
  563. int rotation_bone,
  564. int fork_bone,
  565. int axis_bone
  566. )
  567. {
  568. // Allow the base class to init
  569. SuspensionElementClass::Init(obj,position_bone,rotation_bone,fork_bone,axis_bone);
  570. // Grab a pointer to the render model
  571. RenderObjClass * model = obj->Peek_Model();
  572. WWASSERT(model != NULL);
  573. // Store the bone indices and capture the bones
  574. RotationBone = rotation_bone;
  575. if (RotationBone != -1) {
  576. model->Capture_Bone(RotationBone);
  577. }
  578. // Compute the radius of the wheel
  579. Matrix3D rotation_tm(1);
  580. if (RotationBone != -1) {
  581. rotation_tm = model->Get_Bone_Transform(RotationBone);
  582. Radius = (WheelP0 - rotation_tm.Get_Translation()).Length();
  583. }
  584. }
  585. /***********************************************************************************************
  586. * WheelClass::Compute_Force_And_Torque -- Computes wheel forces *
  587. * *
  588. * INPUT: *
  589. * *
  590. * OUTPUT: *
  591. * *
  592. * WARNINGS: *
  593. * *
  594. * HISTORY: *
  595. * 12/18/2000 gth : Created. *
  596. *=============================================================================================*/
  597. void WheelClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
  598. {
  599. WWPROFILE("WheelClass::Compute_Force_And_Torque");
  600. if (Get_Flag(FAKE)) return;
  601. // Reset variables
  602. SlipFactor = 1.0f;
  603. SuspensionForce.Set(0,0,0);
  604. TractiveFrictionForce.Set(0,0,0);
  605. LateralFrictionForce.Set(0,0,0);
  606. IdealAngularVelocity = 0.0f;
  607. // Intersect the spring with the world to find our contact point
  608. // If it doesn't hit anything, we are done!
  609. {
  610. WWPROFILE("Intersect_Spring");
  611. Intersect_Spring();
  612. }
  613. if (!Get_Flag(INCONTACT)) {
  614. return;
  615. }
  616. // Rotate the wheel coordinate system by the steering angle
  617. WheelTM.Rotate_Z(SteeringAngle);
  618. // Grab the 'Definition' for our parent for quick access to the constants
  619. const VehiclePhysDefClass * vehicle_def = Parent->Get_VehiclePhysDef();
  620. // Compute the velocity of the contact point both in world space and in
  621. // the wheel's coordinate system
  622. Vector3 pdot,local_pdot;
  623. Parent->Compute_Point_Velocity(Contact,&pdot);
  624. local_pdot = pdot - Vector3::Dot_Product(pdot,Normal)*Normal;
  625. Matrix3D::Inverse_Rotate_Vector(WheelTM,local_pdot,&local_pdot);
  626. IdealAngularVelocity = local_pdot.X / Radius;
  627. // Suspension Force:
  628. Compute_Suspension_Force(pdot,local_pdot,&SuspensionForce);
  629. // Get the load on this wheel. I'm not using the spring force since it is too
  630. // sloppy; instead, I'll just give each wheel an equal share of the vehicle weight.
  631. // Also, compute the lateral components of gravitational acceleration so that
  632. // the wheel can attempt to cancel them.
  633. // TODO: load distribution?
  634. Vector3 gravity(0.0f,0.0f,-Parent->Get_Weight());
  635. gravity /= Parent->Get_Real_Wheel_Count();
  636. Vector3 local_gravity;
  637. Matrix3D::Inverse_Rotate_Vector(WheelTM,gravity,&local_gravity);
  638. float wheel_normal_force = -local_gravity.Z;
  639. wheel_normal_force *= vehicle_def->Get_Traction_Multiplier();
  640. // Friction / Traction Forces (initialized so that they cancel gravity):
  641. // (gth, 8/19/2001) scale the gravity cancellation forces down a bit so they don't cause
  642. // vehicles to slide uphill!
  643. const float GRAVITATION_CANCELLATION_FACTOR = 1.0f;
  644. float tractive_force = -local_gravity.X * GRAVITATION_CANCELLATION_FACTOR;
  645. float lateral_force = -local_gravity.Y * GRAVITATION_CANCELLATION_FACTOR;
  646. // testing:
  647. #if 0
  648. Vector3 fs,ft,fn;
  649. Matrix3D::Rotate_Vector(WheelTM,Vector3(0,0,-local_gravity.Z),&fs);
  650. Matrix3D::Rotate_Vector(WheelTM,Vector3(tractive_force,0,0),&ft);
  651. Matrix3D::Rotate_Vector(WheelTM,Vector3(0,lateral_force,0),&fn);
  652. Vector3 sum = fs+ft+fn-gravity;
  653. if (sum.Length2() < 0.001f) {
  654. int test=0;
  655. }
  656. #endif
  657. Compute_Traction_Forces(local_pdot,wheel_normal_force,&lateral_force,&tractive_force);
  658. // Compute the world-space traction forces
  659. Matrix3D::Rotate_Vector(WheelTM,Vector3(tractive_force,0,0),&TractiveFrictionForce);
  660. Matrix3D::Rotate_Vector(WheelTM,Vector3(0,lateral_force,0),&LateralFrictionForce);
  661. // Apply the final forces and torques
  662. Apply_Forces(force,torque);
  663. }
  664. /***********************************************************************************************
  665. * WheelClass::Compute_Suspension_Force -- Computes the suspension force *
  666. * *
  667. * INPUT: *
  668. * *
  669. * OUTPUT: *
  670. * *
  671. * WARNINGS: *
  672. * *
  673. * HISTORY: *
  674. * 12/18/2000 gth : Created. *
  675. *=============================================================================================*/
  676. void WheelClass::Compute_Suspension_Force(const Vector3 & pdot,const Vector3 & local_pdot,Vector3 * suspension_force)
  677. {
  678. WWPROFILE("WheelClass::Compute_Suspension_Force");
  679. // -----------------------------------------------------------------------------
  680. // Suspension Force:
  681. // - Compute the displacement of the wheel along the spring axis
  682. // - Compute the velocity of the wheel projected onto the spring axis
  683. // - Compute the spring/damper force
  684. // -----------------------------------------------------------------------------
  685. Vector3 spring_dir;
  686. WheelTM.Get_Z_Vector(&spring_dir); // spring_dir points from the wheel up to the body
  687. float dv = Vector3::Dot_Product(pdot,spring_dir);
  688. float dx = Vector3::Dot_Product(WheelP0 - Contact,spring_dir) - SpringLength;
  689. float sforce = - SpringConstant*dx - DampingCoefficient*dv;
  690. // Clamp the suspension force to produce at most an acceleration of MAX_SUSPENSION_ACCEL
  691. float max_sforce = MAX_SUSPENSION_ACCEL * Parent->Get_Mass() / WWMath::Max(Parent->Get_Real_Wheel_Count(),1.0f);
  692. sforce = WWMath::Clamp(sforce,-max_sforce,max_sforce);
  693. *suspension_force = Normal;
  694. *suspension_force *= sforce;
  695. }
  696. /***********************************************************************************************
  697. * WheelClass::Apply_Forces -- Computes the final forces and adds them in. *
  698. * *
  699. * INPUT: *
  700. * *
  701. * OUTPUT: *
  702. * *
  703. * WARNINGS: *
  704. * *
  705. * HISTORY: *
  706. * 12/18/2000 gth : Created. *
  707. *=============================================================================================*/
  708. void WheelClass::Apply_Forces(Vector3 * force,Vector3 * torque)
  709. {
  710. WWPROFILE("WheelClass::Apply_Forces");
  711. // Grab the 'Definition' for our parent for quick access to the constants
  712. const VehiclePhysDefClass * vehicle_def = Parent->Get_VehiclePhysDef();
  713. // -----------------------------------------------------------------------------
  714. // Compute the final forces and torques
  715. // I'm applying the suspension force and lateral friction forces in the X-Y plane of
  716. // the CM of the vehicle since we really don't like vehicles that tip over. May even
  717. // need to add a self-balancing force to keep the vehicle from *ever* fliping over...
  718. // -----------------------------------------------------------------------------
  719. Vector3 wheel_pos;
  720. Vector3 r_lateral; // moment arm for lateral forces
  721. Vector3 r_tractive; // moment arm for tractive forces
  722. ObjWheelTM.Get_Translation(&wheel_pos);
  723. wheel_pos.Z = -vehicle_def->Get_Lateral_Moment_Arm();
  724. Matrix3D::Rotate_Vector(Parent->Get_Transform(),wheel_pos,&r_lateral);
  725. wheel_pos.Z = -vehicle_def->Get_Tractive_Moment_Arm();
  726. Matrix3D::Rotate_Vector(Parent->Get_Transform(),wheel_pos,&r_tractive);
  727. // Apply the suspension force in the x-y plane of the CM (actually doesn't matter...)
  728. *force += SuspensionForce;
  729. *torque += Vector3::Cross_Product(r_lateral,SuspensionForce);
  730. // Apply the lateral friction force
  731. *force += LateralFrictionForce;
  732. *torque += Vector3::Cross_Product(r_lateral,LateralFrictionForce);
  733. // Apply the tractive friction force
  734. *force += TractiveFrictionForce;
  735. *torque += Vector3::Cross_Product(r_tractive,TractiveFrictionForce);
  736. // Debugging
  737. #ifdef WWDEBUG
  738. Vector3 position;
  739. Parent->Get_Position(&position);
  740. Parent->Add_Debug_Vector(position + r_lateral,SuspensionForce / Parent->Get_Mass(),SUSPENSION_FORCE_COLOR);
  741. Parent->Add_Debug_Vector(position + r_lateral,LateralFrictionForce / Parent->Get_Mass(),LATERAL_FORCE_COLOR);
  742. Parent->Add_Debug_Vector(position + r_tractive,TractiveFrictionForce / Parent->Get_Mass(),TRACTIVE_FORCE_COLOR);
  743. #endif
  744. }
  745. /***********************************************************************************************
  746. * WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity -- Computes the current angular velocity *
  747. * *
  748. * INPUT: *
  749. * *
  750. * OUTPUT: *
  751. * *
  752. * WARNINGS: *
  753. * *
  754. * HISTORY: *
  755. * 12/18/2000 gth : Created. *
  756. *=============================================================================================*/
  757. float WheelClass::Get_Ideal_Drive_Wheel_Angular_Velocity(float max_avel)
  758. {
  759. if (Get_Flag(INCONTACT)) {
  760. if (IdealAngularVelocity > max_avel) {
  761. return IdealAngularVelocity;
  762. }
  763. }
  764. return max_avel;
  765. }
  766. /*************************************************************************************
  767. WVWheelClass Implementation (Wheeled-Vehicle-Wheel)
  768. *************************************************************************************/
  769. void WVWheelClass::Compute_Traction_Forces
  770. (
  771. const Vector3 & local_pdot,
  772. float wheel_normal_force,
  773. float * set_lateral_force,
  774. float * set_tractive_force
  775. )
  776. {
  777. WWPROFILE("WVWheelClass::Compute_Traction_Forces");
  778. if (wheel_normal_force > 0.0f) {
  779. // Look up the friction coefficient and drag coefficient for this surface
  780. float drag_coefficient = PhysicsConstants::Get_Contact_Drag_Coefficient(
  781. PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
  782. ContactSurface );
  783. float friction_coefficient = PhysicsConstants::Get_Contact_Friction_Coefficient(
  784. PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
  785. ContactSurface );
  786. // Friction force from sliding should act like a proportional controller while the
  787. // engine torque or braking force should just be added in (later to be clamped to the
  788. // friction circle)
  789. float tractive_friction_coefficient = 0.001f;
  790. float lateral_friction_coefficient = friction_coefficient;
  791. if ((Get_Flag(BRAKING) == true) || (Parent->Is_Engine_Enabled() == false)) {
  792. tractive_friction_coefficient = lateral_friction_coefficient;
  793. }
  794. // Friction "penalty" force, proportional to the velocity, tuned so that the force
  795. // will be small when the velocity is small. The lateral and tractive friction forces
  796. // will be set to the MIN between this proportional force and the maximum friction force.
  797. float penalty_k = WHEEL_FRICTION_PENALTY_CONSTANT;
  798. if (Parent->Get_Real_Wheel_Count() < 4) {
  799. penalty_k = BIKE_WHEEL_FRICTION_PENALTY_CONSTANT;
  800. }
  801. float tractive_force = WWMath::Min (
  802. (penalty_k * wheel_normal_force * WWMath::Fabs(local_pdot.X)),
  803. (wheel_normal_force * tractive_friction_coefficient)
  804. );
  805. float lateral_force = WWMath::Min (
  806. (penalty_k * wheel_normal_force * WWMath::Fabs(local_pdot.Y)),
  807. (wheel_normal_force * lateral_friction_coefficient)
  808. );
  809. tractive_force *= -WWMath::Sign(local_pdot.X);
  810. lateral_force *= -WWMath::Sign(local_pdot.Y);
  811. tractive_force -= drag_coefficient * local_pdot.X * local_pdot.X;
  812. if (Get_Flag(ENGINE) && !Get_Flag(BRAKING)) {
  813. tractive_force += AxleTorque / Radius;
  814. }
  815. // Add in the initial lateral and tractive force (gravitation cancelling)
  816. lateral_force += *set_lateral_force;
  817. tractive_force += *set_tractive_force;
  818. // Clamp to the friction circle
  819. Vector2 friction_force(tractive_force,lateral_force);
  820. float max_friction = friction_coefficient * wheel_normal_force;
  821. float max_friction2 = max_friction * max_friction;
  822. float friction2 = friction_force.Length2();
  823. if (friction2 > max_friction2) {
  824. float friction = WWMath::Sqrt(friction2);
  825. friction_force *= max_friction / friction;
  826. SlipFactor = friction / max_friction;
  827. }
  828. // Set the output
  829. *set_tractive_force = friction_force.X;
  830. *set_lateral_force = friction_force.Y;
  831. } else {
  832. *set_tractive_force = 0.0f;
  833. *set_lateral_force = 0.0f;
  834. SlipFactor = 1.0f;
  835. }
  836. }
  837. void WVWheelClass::Update_Model(void)
  838. {
  839. WheelClass::Update_Model();
  840. Roll_Wheel();
  841. }
  842. void WVWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
  843. {
  844. RenderObjClass * model = Parent->Peek_Model();
  845. if (model == NULL) return;
  846. // Set up the position and constraints
  847. WheelClass::Non_Physical_Update(suspension_fraction,rotation);
  848. // Set the rotation
  849. Matrix3D rotation_tm(1);
  850. rotation_tm.Rotate_Z(rotation);
  851. model->Control_Bone(RotationBone,rotation_tm);
  852. }
  853. void WVWheelClass::Roll_Wheel(void)
  854. {
  855. RenderObjClass * model = Parent->Peek_Model();
  856. WheeledVehicleClass * wv_parent = Parent->As_WheeledVehicleClass();
  857. if ((model == NULL) || (wv_parent == NULL) || (RotationBone == -1)) {
  858. return;
  859. }
  860. // if we're braking, the wheels lock (not realistic but fun!)
  861. if (wv_parent->Is_Braking() == false) {
  862. if (Get_Flag(INCONTACT) || (Get_Flag(ENGINE) /*&& Not_In_Neutral*/)) {
  863. // Rotate the wheel based on movement
  864. Vector3 forward;
  865. Vector3 move;
  866. WheelTM.Get_X_Vector(&forward);
  867. Vector3::Subtract(Contact,LastPoint,&move);
  868. float dist = Vector3::Dot_Product(move,forward);
  869. RotationDelta = -dist/Radius;
  870. if (Get_Flag(ENGINE) && (SlipFactor > 1.0f) && (SlipFactor < 100.0f)) {
  871. RotationDelta *= 2.0f * SlipFactor;
  872. }
  873. Rotation += RotationDelta;
  874. WWASSERT(WWMath::Is_Valid_Float(RotationDelta));
  875. WWASSERT(WWMath::Is_Valid_Float(Rotation));
  876. } else {
  877. // Rotate the wheel at the rate it was last rolling and slow it down a little
  878. Rotation += RotationDelta;
  879. RotationDelta *= 0.97f;
  880. WWASSERT(WWMath::Is_Valid_Float(RotationDelta));
  881. WWASSERT(WWMath::Is_Valid_Float(Rotation));
  882. }
  883. }
  884. Matrix3D rotation_tm(1);
  885. rotation_tm.Rotate_Z(Rotation);
  886. model->Control_Bone(RotationBone,rotation_tm);
  887. if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
  888. else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
  889. LastPoint = Contact;
  890. }
  891. /*************************************************************************************
  892. TrackWheelClass Implementation
  893. These wheels differ from WVWheelClass in that they do not roll independently.
  894. They ask their TrackedVehicle parent how much their track has rolled and roll
  895. with it. Also, their friction forces may not behave the same way as wheeled
  896. vehicle wheels... Tanks probably shouldn't slide very much.
  897. *************************************************************************************/
  898. void TrackWheelClass::Compute_Traction_Forces
  899. (
  900. const Vector3 & local_pdot,
  901. float wheel_normal_force,
  902. float * set_lateral_force,
  903. float * set_tractive_force
  904. )
  905. {
  906. WWPROFILE("TrackWheelClass::Compute_Traction_Forces");
  907. // Look up the friction coefficient for this surface
  908. float friction_coefficient = PhysicsConstants::Get_Contact_Friction_Coefficient(
  909. PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
  910. ContactSurface );
  911. // Friction force from sliding should act like a proportional controller while the
  912. // engine torque or braking force should just be added in (later to be clamped to the
  913. // friction circle)
  914. float tractive_friction_coefficient = 0.0001f;
  915. float lateral_friction_coefficient = friction_coefficient;
  916. // Try to stop if our engine is off
  917. if (Parent->Is_Engine_Enabled() == false) {
  918. tractive_friction_coefficient = lateral_friction_coefficient;
  919. }
  920. // Friction "penalty" force, proportional to the velocity, tuned so that the force
  921. // will be small when the velocity is small. The lateral and tractive friction forces
  922. // will be set to the MIN between this proportional force and the maximum friction force.
  923. #if 0
  924. float tractive_force = WWMath::Min (
  925. (TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.X)),
  926. (wheel_normal_force * tractive_friction_coefficient) );
  927. float lateral_force = WWMath::Min (
  928. (TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.Y)),
  929. (wheel_normal_force * lateral_friction_coefficient) );
  930. #else
  931. float tractive_force = TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.X);
  932. float lateral_force = TRACKWHEEL_FRICTION_PENALTY_CONSTANT * wheel_normal_force * WWMath::Fabs(local_pdot.Y);
  933. #endif
  934. tractive_force *= -WWMath::Sign(local_pdot.X);
  935. tractive_force += AxleTorque / Radius;
  936. tractive_force += *set_tractive_force;
  937. lateral_force *= -WWMath::Sign(local_pdot.Y);
  938. lateral_force += *set_lateral_force;
  939. // Clamp to the friction circle
  940. Vector2 wheel_force(tractive_force,lateral_force);
  941. float max_friction = 0.9f * wheel_normal_force;
  942. float max_friction2 = max_friction * max_friction;
  943. float wheel_force2 = wheel_force.Length2();
  944. if (wheel_force2 > max_friction2) {
  945. float initial_wheel_force = WWMath::Sqrt(wheel_force2);
  946. wheel_force *= max_friction / initial_wheel_force;
  947. SlipFactor = initial_wheel_force / max_friction;
  948. }
  949. // Return the result
  950. *set_tractive_force = wheel_force.X;
  951. *set_lateral_force = wheel_force.Y;
  952. }
  953. void TrackWheelClass::Update_Model(void)
  954. {
  955. WheelClass::Update_Model();
  956. Roll_Wheel();
  957. }
  958. void TrackWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
  959. {
  960. RenderObjClass * model = Parent->Peek_Model();
  961. if (model == NULL) return;
  962. // Set up the position and constraints
  963. WheelClass::Non_Physical_Update(suspension_fraction,rotation);
  964. // Set the rotation
  965. Matrix3D rotation_tm(1);
  966. rotation_tm.Rotate_Z(rotation);
  967. model->Control_Bone(RotationBone,rotation_tm);
  968. }
  969. void TrackWheelClass::Roll_Wheel(void)
  970. {
  971. // roll the wheel according to the contact point motion
  972. RenderObjClass * model = Parent->Peek_Model();
  973. TrackedVehicleClass * track_parent = Parent->As_TrackedVehicleClass();
  974. if ((model == NULL) || (track_parent == NULL) || (RotationBone == -1)) {
  975. return;
  976. }
  977. float dist;
  978. if (Get_Flag(LEFT_TRACK)) {
  979. dist = SlipFactor * track_parent->Get_Left_Track_Movement();
  980. } else {
  981. dist = SlipFactor * track_parent->Get_Right_Track_Movement();
  982. }
  983. RotationDelta = -dist/Radius;
  984. Rotation += RotationDelta;
  985. // apply the rotation to the wheel's center bone
  986. Matrix3D rotation_tm(1);
  987. rotation_tm.Rotate_Z(Rotation);
  988. model->Control_Bone(RotationBone,rotation_tm);
  989. if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
  990. else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
  991. LastPoint = Contact;
  992. }
  993. /*************************************************************************************
  994. VTOLWheelClass Implemention
  995. These wheels just don't like to move. Their purpose is to just hold the vehicle
  996. up off the ground when it lands.
  997. *************************************************************************************/
  998. void VTOLWheelClass::Update_Model(void)
  999. {
  1000. WheelClass::Update_Model();
  1001. Roll_Wheel();
  1002. }
  1003. void VTOLWheelClass::Non_Physical_Update(float suspension_fraction,float rotation)
  1004. {
  1005. RenderObjClass * model = Parent->Peek_Model();
  1006. if (model == NULL) return;
  1007. // Set up the position and constraints
  1008. WheelClass::Non_Physical_Update(suspension_fraction,rotation);
  1009. // Set the rotation
  1010. Matrix3D rotation_tm(1);
  1011. rotation_tm.Rotate_Z(rotation);
  1012. model->Control_Bone(RotationBone,rotation_tm);
  1013. }
  1014. void VTOLWheelClass::Compute_Traction_Forces
  1015. (
  1016. const Vector3 & local_pdot,
  1017. float normal_force,
  1018. float * set_lateral_force,
  1019. float * set_tractive_force
  1020. )
  1021. {
  1022. WWPROFILE("VTOLWheelClass::Compute_Traction_Forces");
  1023. // Friction "penalty" force, proportional to the velocity, tuned so that the force
  1024. // will be small when the velocity is small. The lateral and tractive friction forces
  1025. *set_tractive_force -= VTOLWHEEL_FRICTION_PENALTY_CONSTANT * normal_force * local_pdot.X;
  1026. *set_lateral_force -= VTOLWHEEL_FRICTION_PENALTY_CONSTANT * normal_force * local_pdot.Y;
  1027. }
  1028. void VTOLWheelClass::Roll_Wheel(void)
  1029. {
  1030. RenderObjClass * model = Parent->Peek_Model();
  1031. if ((RotationBone != -1) && (model != NULL)) {
  1032. if (Get_Flag(INCONTACT)) {
  1033. // Rotate the wheel based on movement
  1034. Vector3 forward;
  1035. Vector3 move;
  1036. WheelTM.Get_X_Vector(&forward);
  1037. Vector3::Subtract(Contact,LastPoint,&move);
  1038. float dist = Vector3::Dot_Product(move,forward);
  1039. RotationDelta = -dist/Radius;
  1040. Rotation += RotationDelta;
  1041. } else {
  1042. // Rotate the wheel at the rate it was last rolling and slow it down a little
  1043. Rotation += RotationDelta;
  1044. RotationDelta *= 0.97f;
  1045. }
  1046. Matrix3D rotation_tm(1);
  1047. rotation_tm.Rotate_Z(Rotation);
  1048. model->Control_Bone(RotationBone,rotation_tm);
  1049. if (Rotation > 2.0f*(float)WWMATH_PI) Rotation -= 2.0f*(float)WWMATH_PI;
  1050. else if (Rotation < 0.0f) Rotation += 2.0f*(float)WWMATH_PI;
  1051. LastPoint = Contact;
  1052. }
  1053. }