Physics_RigidBody.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code 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. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../../idlib/precompiled.h"
  22. #include "../Game_local.h"
  23. CLASS_DECLARATION( idPhysics_Base, idPhysics_RigidBody )
  24. END_CLASS
  25. const float STOP_SPEED = 10.0f;
  26. #undef RB_TIMINGS
  27. #ifdef RB_TIMINGS
  28. static int lastTimerReset = 0;
  29. static int numRigidBodies = 0;
  30. static idTimer timer_total, timer_collision;
  31. #endif
  32. /*
  33. ================
  34. RigidBodyDerivatives
  35. ================
  36. */
  37. void RigidBodyDerivatives( const float t, const void *clientData, const float *state, float *derivatives ) {
  38. const idPhysics_RigidBody *p = (idPhysics_RigidBody *) clientData;
  39. rigidBodyIState_t *s = (rigidBodyIState_t *) state;
  40. // NOTE: this struct should be build conform rigidBodyIState_t
  41. struct rigidBodyDerivatives_s {
  42. idVec3 linearVelocity;
  43. idMat3 angularMatrix;
  44. idVec3 force;
  45. idVec3 torque;
  46. } *d = (struct rigidBodyDerivatives_s *) derivatives;
  47. idVec3 angularVelocity;
  48. idMat3 inverseWorldInertiaTensor;
  49. inverseWorldInertiaTensor = s->orientation * p->inverseInertiaTensor * s->orientation.Transpose();
  50. angularVelocity = inverseWorldInertiaTensor * s->angularMomentum;
  51. // derivatives
  52. d->linearVelocity = p->inverseMass * s->linearMomentum;
  53. d->angularMatrix = SkewSymmetric( angularVelocity ) * s->orientation;
  54. d->force = - p->linearFriction * s->linearMomentum + p->current.externalForce;
  55. d->torque = - p->angularFriction * s->angularMomentum + p->current.externalTorque;
  56. }
  57. /*
  58. ================
  59. idPhysics_RigidBody::Integrate
  60. Calculate next state from the current state using an integrator.
  61. ================
  62. */
  63. void idPhysics_RigidBody::Integrate( float deltaTime, rigidBodyPState_t &next_ ) {
  64. idVec3 position;
  65. position = current.i.position;
  66. current.i.position += centerOfMass * current.i.orientation;
  67. current.i.orientation.TransposeSelf();
  68. integrator->Evaluate( (float *) &current.i, (float *) &next_.i, 0, deltaTime );
  69. next_.i.orientation.OrthoNormalizeSelf();
  70. // apply gravity
  71. next_.i.linearMomentum += deltaTime * gravityVector * mass;
  72. current.i.orientation.TransposeSelf();
  73. next_.i.orientation.TransposeSelf();
  74. current.i.position = position;
  75. next_.i.position -= centerOfMass * next_.i.orientation;
  76. next_.atRest = current.atRest;
  77. }
  78. /*
  79. ================
  80. idPhysics_RigidBody::CollisionImpulse
  81. Calculates the collision impulse using the velocity relative to the collision object.
  82. The current state should be set to the moment of impact.
  83. ================
  84. */
  85. bool idPhysics_RigidBody::CollisionImpulse( const trace_t &collision, idVec3 &impulse ) {
  86. idVec3 r, linearVelocity, angularVelocity, velocity;
  87. idMat3 inverseWorldInertiaTensor;
  88. float impulseNumerator, impulseDenominator, vel;
  89. impactInfo_t info;
  90. idEntity *ent;
  91. // get info from other entity involved
  92. ent = gameLocal.entities[collision.c.entityNum];
  93. ent->GetImpactInfo( self, collision.c.id, collision.c.point, &info );
  94. // collision point relative to the body center of mass
  95. r = collision.c.point - ( current.i.position + centerOfMass * current.i.orientation );
  96. // the velocity at the collision point
  97. linearVelocity = inverseMass * current.i.linearMomentum;
  98. inverseWorldInertiaTensor = current.i.orientation.Transpose() * inverseInertiaTensor * current.i.orientation;
  99. angularVelocity = inverseWorldInertiaTensor * current.i.angularMomentum;
  100. velocity = linearVelocity + angularVelocity.Cross(r);
  101. // subtract velocity of other entity
  102. velocity -= info.velocity;
  103. // velocity in normal direction
  104. vel = velocity * collision.c.normal;
  105. if ( vel > -STOP_SPEED ) {
  106. impulseNumerator = STOP_SPEED;
  107. }
  108. else {
  109. impulseNumerator = -( 1.0f + bouncyness ) * vel;
  110. }
  111. impulseDenominator = inverseMass + ( ( inverseWorldInertiaTensor * r.Cross( collision.c.normal ) ).Cross( r ) * collision.c.normal );
  112. if ( info.invMass ) {
  113. impulseDenominator += info.invMass + ( ( info.invInertiaTensor * info.position.Cross( collision.c.normal ) ).Cross( info.position ) * collision.c.normal );
  114. }
  115. impulse = (impulseNumerator / impulseDenominator) * collision.c.normal;
  116. // update linear and angular momentum with impulse
  117. current.i.linearMomentum += impulse;
  118. current.i.angularMomentum += r.Cross(impulse);
  119. // if no movement at all don't blow up
  120. if ( collision.fraction < 0.0001f ) {
  121. current.i.linearMomentum *= 0.5f;
  122. current.i.angularMomentum *= 0.5f;
  123. }
  124. // callback to self to let the entity know about the collision
  125. return self->Collide( collision, velocity );
  126. }
  127. /*
  128. ================
  129. idPhysics_RigidBody::CheckForCollisions
  130. Check for collisions between the current and next state.
  131. If there is a collision the next state is set to the state at the moment of impact.
  132. ================
  133. */
  134. bool idPhysics_RigidBody::CheckForCollisions( const float deltaTime, rigidBodyPState_t &next_, trace_t &collision ) {
  135. //#define TEST_COLLISION_DETECTION
  136. idMat3 axis;
  137. idRotation rotation;
  138. bool collided = false;
  139. #ifdef TEST_COLLISION_DETECTION
  140. bool startsolid;
  141. if ( gameLocal.clip.Contents( current.i.position, clipModel, current.i.orientation, clipMask, self ) ) {
  142. startsolid = true;
  143. }
  144. #endif
  145. TransposeMultiply( current.i.orientation, next_.i.orientation, axis );
  146. rotation = axis.ToRotation();
  147. rotation.SetOrigin( current.i.position );
  148. // if there was a collision
  149. if ( gameLocal.clip.Motion( collision, current.i.position, next_.i.position, rotation, clipModel, current.i.orientation, clipMask, self ) ) {
  150. // set the next state to the state at the moment of impact
  151. next_.i.position = collision.endpos;
  152. next_.i.orientation = collision.endAxis;
  153. next_.i.linearMomentum = current.i.linearMomentum;
  154. next_.i.angularMomentum = current.i.angularMomentum;
  155. collided = true;
  156. }
  157. #ifdef TEST_COLLISION_DETECTION
  158. if ( gameLocal.clip.Contents( next.i.position, clipModel, next_.i.orientation, clipMask, self ) ) {
  159. if ( !startsolid ) {
  160. int bah = 1;
  161. }
  162. }
  163. #endif
  164. return collided;
  165. }
  166. /*
  167. ================
  168. idPhysics_RigidBody::ContactFriction
  169. Does not solve friction for multiple simultaneous contacts but applies contact friction in isolation.
  170. Uses absolute velocity at the contact points instead of the velocity relative to the contact object.
  171. ================
  172. */
  173. void idPhysics_RigidBody::ContactFriction( float deltaTime ) {
  174. int i;
  175. float magnitude, impulseNumerator, impulseDenominator;
  176. idMat3 inverseWorldInertiaTensor;
  177. idVec3 linearVelocity, angularVelocity;
  178. idVec3 massCenter, r, velocity, normal, impulse, normalVelocity;
  179. inverseWorldInertiaTensor = current.i.orientation.Transpose() * inverseInertiaTensor * current.i.orientation;
  180. massCenter = current.i.position + centerOfMass * current.i.orientation;
  181. for ( i = 0; i < contacts.Num(); i++ ) {
  182. r = contacts[i].point - massCenter;
  183. // calculate velocity at contact point
  184. linearVelocity = inverseMass * current.i.linearMomentum;
  185. angularVelocity = inverseWorldInertiaTensor * current.i.angularMomentum;
  186. velocity = linearVelocity + angularVelocity.Cross(r);
  187. // velocity along normal vector
  188. normalVelocity = ( velocity * contacts[i].normal ) * contacts[i].normal;
  189. // calculate friction impulse
  190. normal = -( velocity - normalVelocity );
  191. magnitude = normal.Normalize();
  192. impulseNumerator = contactFriction * magnitude;
  193. impulseDenominator = inverseMass + ( ( inverseWorldInertiaTensor * r.Cross( normal ) ).Cross( r ) * normal );
  194. impulse = (impulseNumerator / impulseDenominator) * normal;
  195. // apply friction impulse
  196. current.i.linearMomentum += impulse;
  197. current.i.angularMomentum += r.Cross(impulse);
  198. // if moving towards the surface at the contact point
  199. if ( normalVelocity * contacts[i].normal < 0.0f ) {
  200. // calculate impulse
  201. normal = -normalVelocity;
  202. impulseNumerator = normal.Normalize();
  203. impulseDenominator = inverseMass + ( ( inverseWorldInertiaTensor * r.Cross( normal ) ).Cross( r ) * normal );
  204. impulse = (impulseNumerator / impulseDenominator) * normal;
  205. // apply impulse
  206. current.i.linearMomentum += impulse;
  207. current.i.angularMomentum += r.Cross( impulse );
  208. }
  209. }
  210. }
  211. /*
  212. ================
  213. idPhysics_RigidBody::TestIfAtRest
  214. Returns true if the body is considered at rest.
  215. Does not catch all cases where the body is at rest but is generally good enough.
  216. ================
  217. */
  218. bool idPhysics_RigidBody::TestIfAtRest() const {
  219. int i;
  220. float gv;
  221. idVec3 v, av, normal, point;
  222. idMat3 inverseWorldInertiaTensor;
  223. idFixedWinding contactWinding;
  224. if ( current.atRest >= 0 ) {
  225. return true;
  226. }
  227. // need at least 3 contact points to come to rest
  228. if ( contacts.Num() < 3 ) {
  229. return false;
  230. }
  231. // get average contact plane normal
  232. normal.Zero();
  233. for ( i = 0; i < contacts.Num(); i++ ) {
  234. normal += contacts[i].normal;
  235. }
  236. normal /= (float) contacts.Num();
  237. normal.Normalize();
  238. // if on a too steep surface
  239. if ( (normal * gravityNormal) > -0.7f ) {
  240. return false;
  241. }
  242. // create bounds for contact points
  243. contactWinding.Clear();
  244. for ( i = 0; i < contacts.Num(); i++ ) {
  245. // project point onto plane through origin orthogonal to the gravity
  246. point = contacts[i].point - (contacts[i].point * gravityNormal) * gravityNormal;
  247. contactWinding.AddToConvexHull( point, gravityNormal );
  248. }
  249. // need at least 3 contact points to come to rest
  250. if ( contactWinding.GetNumPoints() < 3 ) {
  251. return false;
  252. }
  253. // center of mass in world space
  254. point = current.i.position + centerOfMass * current.i.orientation;
  255. point -= (point * gravityNormal) * gravityNormal;
  256. // if the point is not inside the winding
  257. if ( !contactWinding.PointInside( gravityNormal, point, 0 ) ) {
  258. return false;
  259. }
  260. // linear velocity of body
  261. v = inverseMass * current.i.linearMomentum;
  262. // linear velocity in gravity direction
  263. gv = v * gravityNormal;
  264. // linear velocity orthogonal to gravity direction
  265. v -= gv * gravityNormal;
  266. // if too much velocity orthogonal to gravity direction
  267. if ( v.Length() > STOP_SPEED ) {
  268. return false;
  269. }
  270. // if too much velocity in gravity direction
  271. if ( gv > 2.0f * STOP_SPEED || gv < -2.0f * STOP_SPEED ) {
  272. return false;
  273. }
  274. // calculate rotational velocity
  275. inverseWorldInertiaTensor = current.i.orientation * inverseInertiaTensor * current.i.orientation.Transpose();
  276. av = inverseWorldInertiaTensor * current.i.angularMomentum;
  277. // if too much rotational velocity
  278. if ( av.LengthSqr() > STOP_SPEED ) {
  279. return false;
  280. }
  281. return true;
  282. }
  283. /*
  284. ================
  285. idPhysics_RigidBody::DropToFloorAndRest
  286. Drops the object straight down to the floor and verifies if the object is at rest on the floor.
  287. ================
  288. */
  289. void idPhysics_RigidBody::DropToFloorAndRest() {
  290. idVec3 down;
  291. trace_t tr;
  292. if ( testSolid ) {
  293. testSolid = false;
  294. if ( gameLocal.clip.Contents( current.i.position, clipModel, current.i.orientation, clipMask, self ) ) {
  295. gameLocal.DWarning( "rigid body in solid for entity '%s' type '%s' at (%s)",
  296. self->name.c_str(), self->GetType()->classname, current.i.position.ToString(0) );
  297. Rest();
  298. dropToFloor = false;
  299. return;
  300. }
  301. }
  302. // put the body on the floor
  303. down = current.i.position + gravityNormal * 128.0f;
  304. gameLocal.clip.Translation( tr, current.i.position, down, clipModel, current.i.orientation, clipMask, self );
  305. current.i.position = tr.endpos;
  306. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), tr.endpos, current.i.orientation );
  307. // if on the floor already
  308. if ( tr.fraction == 0.0f ) {
  309. // test if we are really at rest
  310. EvaluateContacts();
  311. if ( !TestIfAtRest() ) {
  312. gameLocal.DWarning( "rigid body not at rest for entity '%s' type '%s' at (%s)",
  313. self->name.c_str(), self->GetType()->classname, current.i.position.ToString(0) );
  314. }
  315. Rest();
  316. dropToFloor = false;
  317. } else if ( IsOutsideWorld() ) {
  318. gameLocal.Warning( "rigid body outside world bounds for entity '%s' type '%s' at (%s)",
  319. self->name.c_str(), self->GetType()->classname, current.i.position.ToString(0) );
  320. Rest();
  321. dropToFloor = false;
  322. }
  323. }
  324. /*
  325. ================
  326. idPhysics_RigidBody::DebugDraw
  327. ================
  328. */
  329. void idPhysics_RigidBody::DebugDraw() {
  330. if ( rb_showBodies.GetBool() || ( rb_showActive.GetBool() && current.atRest < 0 ) ) {
  331. collisionModelManager->DrawModel( clipModel->Handle(), clipModel->GetOrigin(), clipModel->GetAxis(), vec3_origin, 0.0f );
  332. }
  333. if ( rb_showMass.GetBool() ) {
  334. gameRenderWorld->DrawText( va( "\n%1.2f", mass ), current.i.position, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
  335. }
  336. if ( rb_showInertia.GetBool() ) {
  337. idMat3 &I = inertiaTensor;
  338. gameRenderWorld->DrawText( va( "\n\n\n( %.1f %.1f %.1f )\n( %.1f %.1f %.1f )\n( %.1f %.1f %.1f )",
  339. I[0].x, I[0].y, I[0].z,
  340. I[1].x, I[1].y, I[1].z,
  341. I[2].x, I[2].y, I[2].z ),
  342. current.i.position, 0.05f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
  343. }
  344. if ( rb_showVelocity.GetBool() ) {
  345. DrawVelocity( clipModel->GetId(), 0.1f, 4.0f );
  346. }
  347. }
  348. /*
  349. ================
  350. idPhysics_RigidBody::idPhysics_RigidBody
  351. ================
  352. */
  353. idPhysics_RigidBody::idPhysics_RigidBody() {
  354. // set default rigid body properties
  355. SetClipMask( MASK_SOLID );
  356. SetBouncyness( 0.6f );
  357. SetFriction( 0.6f, 0.6f, 0.0f );
  358. clipModel = NULL;
  359. current.atRest = -1;
  360. current.lastTimeStep = 0.0f;
  361. current.i.position.Zero();
  362. current.i.orientation.Identity();
  363. current.i.linearMomentum.Zero();
  364. current.i.angularMomentum.Zero();
  365. saved = current;
  366. mass = 1.0f;
  367. inverseMass = 1.0f;
  368. centerOfMass.Zero();
  369. inertiaTensor.Identity();
  370. inverseInertiaTensor.Identity();
  371. // use the least expensive euler integrator
  372. integrator = new (TAG_PHYSICS) idODE_Euler( sizeof(rigidBodyIState_t) / sizeof(float), RigidBodyDerivatives, this );
  373. dropToFloor = false;
  374. noImpact = false;
  375. noContact = false;
  376. hasMaster = false;
  377. isOrientated = false;
  378. #ifdef RB_TIMINGS
  379. lastTimerReset = 0;
  380. #endif
  381. }
  382. /*
  383. ================
  384. idPhysics_RigidBody::~idPhysics_RigidBody
  385. ================
  386. */
  387. idPhysics_RigidBody::~idPhysics_RigidBody() {
  388. if ( clipModel ) {
  389. delete clipModel;
  390. clipModel = NULL;
  391. }
  392. delete integrator;
  393. }
  394. /*
  395. ================
  396. idPhysics_RigidBody_SavePState
  397. ================
  398. */
  399. void idPhysics_RigidBody_SavePState( idSaveGame *savefile, const rigidBodyPState_t &state ) {
  400. savefile->WriteInt( state.atRest );
  401. savefile->WriteFloat( state.lastTimeStep );
  402. savefile->WriteVec3( state.localOrigin );
  403. savefile->WriteMat3( state.localAxis );
  404. savefile->WriteVec6( state.pushVelocity );
  405. savefile->WriteVec3( state.externalForce );
  406. savefile->WriteVec3( state.externalTorque );
  407. savefile->WriteVec3( state.i.position );
  408. savefile->WriteMat3( state.i.orientation );
  409. savefile->WriteVec3( state.i.linearMomentum );
  410. savefile->WriteVec3( state.i.angularMomentum );
  411. }
  412. /*
  413. ================
  414. idPhysics_RigidBody_RestorePState
  415. ================
  416. */
  417. void idPhysics_RigidBody_RestorePState( idRestoreGame *savefile, rigidBodyPState_t &state ) {
  418. savefile->ReadInt( state.atRest );
  419. savefile->ReadFloat( state.lastTimeStep );
  420. savefile->ReadVec3( state.localOrigin );
  421. savefile->ReadMat3( state.localAxis );
  422. savefile->ReadVec6( state.pushVelocity );
  423. savefile->ReadVec3( state.externalForce );
  424. savefile->ReadVec3( state.externalTorque );
  425. savefile->ReadVec3( state.i.position );
  426. savefile->ReadMat3( state.i.orientation );
  427. savefile->ReadVec3( state.i.linearMomentum );
  428. savefile->ReadVec3( state.i.angularMomentum );
  429. }
  430. /*
  431. ================
  432. idPhysics_RigidBody::Save
  433. ================
  434. */
  435. void idPhysics_RigidBody::Save( idSaveGame *savefile ) const {
  436. idPhysics_RigidBody_SavePState( savefile, current );
  437. idPhysics_RigidBody_SavePState( savefile, saved );
  438. savefile->WriteFloat( linearFriction );
  439. savefile->WriteFloat( angularFriction );
  440. savefile->WriteFloat( contactFriction );
  441. savefile->WriteFloat( bouncyness );
  442. savefile->WriteClipModel( clipModel );
  443. savefile->WriteFloat( mass );
  444. savefile->WriteFloat( inverseMass );
  445. savefile->WriteVec3( centerOfMass );
  446. savefile->WriteMat3( inertiaTensor );
  447. savefile->WriteMat3( inverseInertiaTensor );
  448. savefile->WriteBool( dropToFloor );
  449. savefile->WriteBool( testSolid );
  450. savefile->WriteBool( noImpact );
  451. savefile->WriteBool( noContact );
  452. savefile->WriteBool( hasMaster );
  453. savefile->WriteBool( isOrientated );
  454. }
  455. /*
  456. ================
  457. idPhysics_RigidBody::Restore
  458. ================
  459. */
  460. void idPhysics_RigidBody::Restore( idRestoreGame *savefile ) {
  461. idPhysics_RigidBody_RestorePState( savefile, current );
  462. idPhysics_RigidBody_RestorePState( savefile, saved );
  463. savefile->ReadFloat( linearFriction );
  464. savefile->ReadFloat( angularFriction );
  465. savefile->ReadFloat( contactFriction );
  466. savefile->ReadFloat( bouncyness );
  467. savefile->ReadClipModel( clipModel );
  468. savefile->ReadFloat( mass );
  469. savefile->ReadFloat( inverseMass );
  470. savefile->ReadVec3( centerOfMass );
  471. savefile->ReadMat3( inertiaTensor );
  472. savefile->ReadMat3( inverseInertiaTensor );
  473. savefile->ReadBool( dropToFloor );
  474. savefile->ReadBool( testSolid );
  475. savefile->ReadBool( noImpact );
  476. savefile->ReadBool( noContact );
  477. savefile->ReadBool( hasMaster );
  478. savefile->ReadBool( isOrientated );
  479. }
  480. /*
  481. ================
  482. idPhysics_RigidBody::SetClipModel
  483. ================
  484. */
  485. #define MAX_INERTIA_SCALE 10.0f
  486. void idPhysics_RigidBody::SetClipModel( idClipModel *model, const float density, int id, bool freeOld ) {
  487. int minIndex;
  488. idMat3 inertiaScale;
  489. assert( self );
  490. assert( model ); // we need a clip model
  491. assert( model->IsTraceModel() ); // and it should be a trace model
  492. assert( density > 0.0f ); // density should be valid
  493. if ( clipModel && clipModel != model && freeOld ) {
  494. delete clipModel;
  495. }
  496. clipModel = model;
  497. clipModel->Link( gameLocal.clip, self, 0, current.i.position, current.i.orientation );
  498. // get mass properties from the trace model
  499. clipModel->GetMassProperties( density, mass, centerOfMass, inertiaTensor );
  500. // check whether or not the clip model has valid mass properties
  501. if ( mass <= 0.0f || IEEE_FLT_IS_NAN( mass ) ) {
  502. gameLocal.Warning( "idPhysics_RigidBody::SetClipModel: invalid mass for entity '%s' type '%s'",
  503. self->name.c_str(), self->GetType()->classname );
  504. mass = 1.0f;
  505. centerOfMass.Zero();
  506. inertiaTensor.Identity();
  507. }
  508. // check whether or not the inertia tensor is balanced
  509. minIndex = Min3Index( inertiaTensor[0][0], inertiaTensor[1][1], inertiaTensor[2][2] );
  510. inertiaScale.Identity();
  511. inertiaScale[0][0] = inertiaTensor[0][0] / inertiaTensor[minIndex][minIndex];
  512. inertiaScale[1][1] = inertiaTensor[1][1] / inertiaTensor[minIndex][minIndex];
  513. inertiaScale[2][2] = inertiaTensor[2][2] / inertiaTensor[minIndex][minIndex];
  514. if ( inertiaScale[0][0] > MAX_INERTIA_SCALE || inertiaScale[1][1] > MAX_INERTIA_SCALE || inertiaScale[2][2] > MAX_INERTIA_SCALE ) {
  515. gameLocal.DWarning( "idPhysics_RigidBody::SetClipModel: unbalanced inertia tensor for entity '%s' type '%s'",
  516. self->name.c_str(), self->GetType()->classname );
  517. float min = inertiaTensor[minIndex][minIndex] * MAX_INERTIA_SCALE;
  518. inertiaScale[(minIndex+1)%3][(minIndex+1)%3] = min / inertiaTensor[(minIndex+1)%3][(minIndex+1)%3];
  519. inertiaScale[(minIndex+2)%3][(minIndex+2)%3] = min / inertiaTensor[(minIndex+2)%3][(minIndex+2)%3];
  520. inertiaTensor *= inertiaScale;
  521. }
  522. inverseMass = 1.0f / mass;
  523. inverseInertiaTensor = inertiaTensor.Inverse() * ( 1.0f / 6.0f );
  524. current.i.linearMomentum.Zero();
  525. current.i.angularMomentum.Zero();
  526. }
  527. /*
  528. ================
  529. idPhysics_RigidBody::GetClipModel
  530. ================
  531. */
  532. idClipModel *idPhysics_RigidBody::GetClipModel( int id ) const {
  533. return clipModel;
  534. }
  535. /*
  536. ================
  537. idPhysics_RigidBody::GetNumClipModels
  538. ================
  539. */
  540. int idPhysics_RigidBody::GetNumClipModels() const {
  541. return 1;
  542. }
  543. /*
  544. ================
  545. idPhysics_RigidBody::SetMass
  546. ================
  547. */
  548. void idPhysics_RigidBody::SetMass( float mass, int id ) {
  549. assert( mass > 0.0f );
  550. inertiaTensor *= mass / this->mass;
  551. inverseInertiaTensor = inertiaTensor.Inverse() * (1.0f / 6.0f);
  552. this->mass = mass;
  553. inverseMass = 1.0f / mass;
  554. }
  555. /*
  556. ================
  557. idPhysics_RigidBody::GetMass
  558. ================
  559. */
  560. float idPhysics_RigidBody::GetMass( int id ) const {
  561. return mass;
  562. }
  563. /*
  564. ================
  565. idPhysics_RigidBody::SetFriction
  566. ================
  567. */
  568. void idPhysics_RigidBody::SetFriction( const float linear, const float angular, const float contact ) {
  569. if ( linear < 0.0f || linear > 1.0f ||
  570. angular < 0.0f || angular > 1.0f ||
  571. contact < 0.0f || contact > 1.0f ) {
  572. return;
  573. }
  574. linearFriction = linear;
  575. angularFriction = angular;
  576. contactFriction = contact;
  577. }
  578. /*
  579. ================
  580. idPhysics_RigidBody::SetBouncyness
  581. ================
  582. */
  583. void idPhysics_RigidBody::SetBouncyness( const float b ) {
  584. if ( b < 0.0f || b > 1.0f ) {
  585. return;
  586. }
  587. bouncyness = b;
  588. }
  589. /*
  590. ================
  591. idPhysics_RigidBody::Rest
  592. ================
  593. */
  594. void idPhysics_RigidBody::Rest() {
  595. current.atRest = gameLocal.time;
  596. current.i.linearMomentum.Zero();
  597. current.i.angularMomentum.Zero();
  598. self->BecomeInactive( TH_PHYSICS );
  599. }
  600. /*
  601. ================
  602. idPhysics_RigidBody::DropToFloor
  603. ================
  604. */
  605. void idPhysics_RigidBody::DropToFloor() {
  606. dropToFloor = true;
  607. testSolid = true;
  608. }
  609. /*
  610. ================
  611. idPhysics_RigidBody::NoContact
  612. ================
  613. */
  614. void idPhysics_RigidBody::NoContact() {
  615. noContact = true;
  616. }
  617. /*
  618. ================
  619. idPhysics_RigidBody::Activate
  620. ================
  621. */
  622. void idPhysics_RigidBody::Activate() {
  623. current.atRest = -1;
  624. self->BecomeActive( TH_PHYSICS );
  625. }
  626. /*
  627. ================
  628. idPhysics_RigidBody::PutToRest
  629. put to rest untill something collides with this physics object
  630. ================
  631. */
  632. void idPhysics_RigidBody::PutToRest() {
  633. Rest();
  634. }
  635. /*
  636. ================
  637. idPhysics_RigidBody::EnableImpact
  638. ================
  639. */
  640. void idPhysics_RigidBody::EnableImpact() {
  641. noImpact = false;
  642. }
  643. /*
  644. ================
  645. idPhysics_RigidBody::DisableImpact
  646. ================
  647. */
  648. void idPhysics_RigidBody::DisableImpact() {
  649. noImpact = true;
  650. }
  651. /*
  652. ================
  653. idPhysics_RigidBody::SetContents
  654. ================
  655. */
  656. void idPhysics_RigidBody::SetContents( int contents, int id ) {
  657. clipModel->SetContents( contents );
  658. }
  659. /*
  660. ================
  661. idPhysics_RigidBody::GetContents
  662. ================
  663. */
  664. int idPhysics_RigidBody::GetContents( int id ) const {
  665. return clipModel->GetContents();
  666. }
  667. /*
  668. ================
  669. idPhysics_RigidBody::GetBounds
  670. ================
  671. */
  672. const idBounds &idPhysics_RigidBody::GetBounds( int id ) const {
  673. return clipModel->GetBounds();
  674. }
  675. /*
  676. ================
  677. idPhysics_RigidBody::GetAbsBounds
  678. ================
  679. */
  680. const idBounds &idPhysics_RigidBody::GetAbsBounds( int id ) const {
  681. return clipModel->GetAbsBounds();
  682. }
  683. /*
  684. ================
  685. idPhysics_RigidBody::Evaluate
  686. Evaluate the impulse based rigid body physics.
  687. When a collision occurs an impulse is applied at the moment of impact but
  688. the remaining time after the collision is ignored.
  689. ================
  690. */
  691. bool idPhysics_RigidBody::Evaluate( int timeStepMSec, int endTimeMSec ) {
  692. rigidBodyPState_t next_step;
  693. idAngles angles;
  694. trace_t collision;
  695. idVec3 impulse;
  696. idEntity *ent;
  697. idVec3 oldOrigin, masterOrigin;
  698. idMat3 oldAxis, masterAxis;
  699. float timeStep;
  700. bool collided, cameToRest = false;
  701. timeStep = MS2SEC( timeStepMSec );
  702. current.lastTimeStep = timeStep;
  703. if ( hasMaster ) {
  704. oldOrigin = current.i.position;
  705. oldAxis = current.i.orientation;
  706. self->GetMasterPosition( masterOrigin, masterAxis );
  707. current.i.position = masterOrigin + current.localOrigin * masterAxis;
  708. if ( isOrientated ) {
  709. current.i.orientation = current.localAxis * masterAxis;
  710. }
  711. else {
  712. current.i.orientation = current.localAxis;
  713. }
  714. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation );
  715. current.i.linearMomentum = mass * ( ( current.i.position - oldOrigin ) / timeStep );
  716. current.i.angularMomentum = inertiaTensor * ( ( current.i.orientation * oldAxis.Transpose() ).ToAngularVelocity() / timeStep );
  717. current.externalForce.Zero();
  718. current.externalTorque.Zero();
  719. return ( current.i.position != oldOrigin || current.i.orientation != oldAxis );
  720. }
  721. // if the body is at rest
  722. if ( current.atRest >= 0 || timeStep <= 0.0f ) {
  723. DebugDraw();
  724. return false;
  725. }
  726. // if putting the body to rest
  727. if ( dropToFloor ) {
  728. DropToFloorAndRest();
  729. current.externalForce.Zero();
  730. current.externalTorque.Zero();
  731. return true;
  732. }
  733. #ifdef RB_TIMINGS
  734. timer_total.Start();
  735. #endif
  736. // move the rigid body velocity into the frame of a pusher
  737. // current.i.linearMomentum -= current.pushVelocity.SubVec3( 0 ) * mass;
  738. // current.i.angularMomentum -= current.pushVelocity.SubVec3( 1 ) * inertiaTensor;
  739. clipModel->Unlink();
  740. next_step = current;
  741. // calculate next position and orientation
  742. Integrate( timeStep, next_step );
  743. #ifdef RB_TIMINGS
  744. timer_collision.Start();
  745. #endif
  746. // check for collisions from the current to the next state
  747. collided = CheckForCollisions( timeStep, next_step, collision );
  748. #ifdef RB_TIMINGS
  749. timer_collision.Stop();
  750. #endif
  751. // set the new state
  752. current = next_step;
  753. if ( collided ) {
  754. // apply collision impulse
  755. if ( CollisionImpulse( collision, impulse ) ) {
  756. current.atRest = gameLocal.time;
  757. }
  758. }
  759. // update the position of the clip model
  760. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation );
  761. DebugDraw();
  762. if ( !noContact ) {
  763. #ifdef RB_TIMINGS
  764. timer_collision.Start();
  765. #endif
  766. // get contacts
  767. EvaluateContacts();
  768. #ifdef RB_TIMINGS
  769. timer_collision.Stop();
  770. #endif
  771. // check if the body has come to rest
  772. if ( TestIfAtRest() ) {
  773. // put to rest
  774. Rest();
  775. cameToRest = true;
  776. } else {
  777. // apply contact friction
  778. ContactFriction( timeStep );
  779. }
  780. }
  781. if ( current.atRest < 0 ) {
  782. ActivateContactEntities();
  783. }
  784. if ( collided ) {
  785. // if the rigid body didn't come to rest or the other entity is not at rest
  786. ent = gameLocal.entities[collision.c.entityNum];
  787. if ( ent && ( !cameToRest || !ent->IsAtRest() ) ) {
  788. // apply impact to other entity
  789. ent->ApplyImpulse( self, collision.c.id, collision.c.point, -impulse );
  790. }
  791. }
  792. // move the rigid body velocity back into the world frame
  793. // current.i.linearMomentum += current.pushVelocity.SubVec3( 0 ) * mass;
  794. // current.i.angularMomentum += current.pushVelocity.SubVec3( 1 ) * inertiaTensor;
  795. current.pushVelocity.Zero();
  796. current.lastTimeStep = timeStep;
  797. current.externalForce.Zero();
  798. current.externalTorque.Zero();
  799. if ( IsOutsideWorld() ) {
  800. gameLocal.Warning( "rigid body moved outside world bounds for entity '%s' type '%s' at (%s)",
  801. self->name.c_str(), self->GetType()->classname, current.i.position.ToString(0) );
  802. Rest();
  803. }
  804. #ifdef RB_TIMINGS
  805. timer_total.Stop();
  806. if ( rb_showTimings->integer == 1 ) {
  807. gameLocal.Printf( "%12s: t %1.4f cd %1.4f\n",
  808. self->name.c_str(),
  809. timer_total.Milliseconds(), timer_collision.Milliseconds() );
  810. lastTimerReset = 0;
  811. }
  812. else if ( rb_showTimings->integer == 2 ) {
  813. numRigidBodies++;
  814. if ( endTimeMSec > lastTimerReset ) {
  815. gameLocal.Printf( "rb %d: t %1.4f cd %1.4f\n",
  816. numRigidBodies,
  817. timer_total.Milliseconds(), timer_collision.Milliseconds() );
  818. }
  819. }
  820. if ( endTimeMSec > lastTimerReset ) {
  821. lastTimerReset = endTimeMSec;
  822. numRigidBodies = 0;
  823. timer_total.Clear();
  824. timer_collision.Clear();
  825. }
  826. #endif
  827. return true;
  828. }
  829. /*
  830. ================
  831. idPhysics_RigidBody::Interpolate
  832. Simply interpolate between snapshots of the state of the rigid body
  833. for MP clients.
  834. ================
  835. */
  836. bool idPhysics_RigidBody::Interpolate( const float fraction ) {
  837. if ( !self ) {
  838. return false;
  839. }
  840. if ( self->GetInterpolationBehavior() == idEntity::USE_LATEST_SNAP_ONLY ) {
  841. current = next;
  842. return true;
  843. } else if ( self->GetInterpolationBehavior() == idEntity::USE_INTERPOLATION ) {
  844. current.i.position = Lerp( previous.i.position, next.i.position, fraction );
  845. current.i.orientation = idQuat().Slerp( previous.i.orientation.ToQuat(), next.i.orientation.ToQuat(), fraction ).ToMat3();
  846. current.i.linearMomentum = Lerp( previous.i.linearMomentum, next.i.linearMomentum, fraction );
  847. return true;
  848. }
  849. return false;
  850. }
  851. /*
  852. ================
  853. idPhysics_RigidBody::ResetInterpolationState
  854. ================
  855. */
  856. void idPhysics_RigidBody::ResetInterpolationState( const idVec3 & origin, const idMat3 & axis ) {
  857. previous = current;
  858. next = current;
  859. }
  860. /*
  861. ================
  862. idPhysics_RigidBody::UpdateTime
  863. ================
  864. */
  865. void idPhysics_RigidBody::UpdateTime( int endTimeMSec ) {
  866. }
  867. /*
  868. ================
  869. idPhysics_RigidBody::GetTime
  870. ================
  871. */
  872. int idPhysics_RigidBody::GetTime() const {
  873. return gameLocal.time;
  874. }
  875. /*
  876. ================
  877. idPhysics_RigidBody::GetImpactInfo
  878. ================
  879. */
  880. void idPhysics_RigidBody::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
  881. idVec3 linearVelocity, angularVelocity;
  882. idMat3 inverseWorldInertiaTensor;
  883. linearVelocity = inverseMass * current.i.linearMomentum;
  884. inverseWorldInertiaTensor = current.i.orientation.Transpose() * inverseInertiaTensor * current.i.orientation;
  885. angularVelocity = inverseWorldInertiaTensor * current.i.angularMomentum;
  886. info->invMass = inverseMass;
  887. info->invInertiaTensor = inverseWorldInertiaTensor;
  888. info->position = point - ( current.i.position + centerOfMass * current.i.orientation );
  889. info->velocity = linearVelocity + angularVelocity.Cross( info->position );
  890. }
  891. /*
  892. ================
  893. idPhysics_RigidBody::ApplyImpulse
  894. ================
  895. */
  896. void idPhysics_RigidBody::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
  897. if ( noImpact ) {
  898. return;
  899. }
  900. current.i.linearMomentum += impulse;
  901. current.i.angularMomentum += ( point - ( current.i.position + centerOfMass * current.i.orientation ) ).Cross( impulse );
  902. Activate();
  903. }
  904. /*
  905. ================
  906. idPhysics_RigidBody::AddForce
  907. ================
  908. */
  909. void idPhysics_RigidBody::AddForce( const int id, const idVec3 &point, const idVec3 &force ) {
  910. if ( noImpact ) {
  911. return;
  912. }
  913. current.externalForce += force;
  914. current.externalTorque += ( point - ( current.i.position + centerOfMass * current.i.orientation ) ).Cross( force );
  915. Activate();
  916. }
  917. /*
  918. ================
  919. idPhysics_RigidBody::IsAtRest
  920. ================
  921. */
  922. bool idPhysics_RigidBody::IsAtRest() const {
  923. return current.atRest >= 0;
  924. }
  925. /*
  926. ================
  927. idPhysics_RigidBody::GetRestStartTime
  928. ================
  929. */
  930. int idPhysics_RigidBody::GetRestStartTime() const {
  931. return current.atRest;
  932. }
  933. /*
  934. ================
  935. idPhysics_RigidBody::IsPushable
  936. ================
  937. */
  938. bool idPhysics_RigidBody::IsPushable() const {
  939. return ( !noImpact && !hasMaster );
  940. }
  941. /*
  942. ================
  943. idPhysics_RigidBody::SaveState
  944. ================
  945. */
  946. void idPhysics_RigidBody::SaveState() {
  947. saved = current;
  948. }
  949. /*
  950. ================
  951. idPhysics_RigidBody::RestoreState
  952. ================
  953. */
  954. void idPhysics_RigidBody::RestoreState() {
  955. current = saved;
  956. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation );
  957. EvaluateContacts();
  958. }
  959. /*
  960. ================
  961. idPhysics::SetOrigin
  962. ================
  963. */
  964. void idPhysics_RigidBody::SetOrigin( const idVec3 &newOrigin, int id ) {
  965. idVec3 masterOrigin;
  966. idMat3 masterAxis;
  967. current.localOrigin = newOrigin;
  968. if ( hasMaster ) {
  969. self->GetMasterPosition( masterOrigin, masterAxis );
  970. current.i.position = masterOrigin + newOrigin * masterAxis;
  971. }
  972. else {
  973. current.i.position = newOrigin;
  974. }
  975. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, clipModel->GetAxis() );
  976. Activate();
  977. }
  978. /*
  979. ================
  980. idPhysics::SetAxis
  981. ================
  982. */
  983. void idPhysics_RigidBody::SetAxis( const idMat3 &newAxis, int id ) {
  984. idVec3 masterOrigin;
  985. idMat3 masterAxis;
  986. current.localAxis = newAxis;
  987. if ( hasMaster && isOrientated ) {
  988. self->GetMasterPosition( masterOrigin, masterAxis );
  989. current.i.orientation = newAxis * masterAxis;
  990. }
  991. else {
  992. current.i.orientation = newAxis;
  993. }
  994. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), clipModel->GetOrigin(), current.i.orientation );
  995. Activate();
  996. }
  997. /*
  998. ================
  999. idPhysics::Move
  1000. ================
  1001. */
  1002. void idPhysics_RigidBody::Translate( const idVec3 &translation, int id ) {
  1003. current.localOrigin += translation;
  1004. current.i.position += translation;
  1005. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, clipModel->GetAxis() );
  1006. Activate();
  1007. }
  1008. /*
  1009. ================
  1010. idPhysics::Rotate
  1011. ================
  1012. */
  1013. void idPhysics_RigidBody::Rotate( const idRotation &rotation, int id ) {
  1014. idVec3 masterOrigin;
  1015. idMat3 masterAxis;
  1016. current.i.orientation *= rotation.ToMat3();
  1017. current.i.position *= rotation;
  1018. if ( hasMaster ) {
  1019. self->GetMasterPosition( masterOrigin, masterAxis );
  1020. current.localAxis *= rotation.ToMat3();
  1021. current.localOrigin = ( current.i.position - masterOrigin ) * masterAxis.Transpose();
  1022. }
  1023. else {
  1024. current.localAxis = current.i.orientation;
  1025. current.localOrigin = current.i.position;
  1026. }
  1027. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation );
  1028. Activate();
  1029. }
  1030. /*
  1031. ================
  1032. idPhysics_RigidBody::GetOrigin
  1033. ================
  1034. */
  1035. const idVec3 &idPhysics_RigidBody::GetOrigin( int id ) const {
  1036. return current.i.position;
  1037. }
  1038. /*
  1039. ================
  1040. idPhysics_RigidBody::GetAxis
  1041. ================
  1042. */
  1043. const idMat3 &idPhysics_RigidBody::GetAxis( int id ) const {
  1044. return current.i.orientation;
  1045. }
  1046. /*
  1047. ================
  1048. idPhysics_RigidBody::SetLinearVelocity
  1049. ================
  1050. */
  1051. void idPhysics_RigidBody::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
  1052. current.i.linearMomentum = newLinearVelocity * mass;
  1053. Activate();
  1054. }
  1055. /*
  1056. ================
  1057. idPhysics_RigidBody::SetAngularVelocity
  1058. ================
  1059. */
  1060. void idPhysics_RigidBody::SetAngularVelocity( const idVec3 &newAngularVelocity, int id ) {
  1061. current.i.angularMomentum = newAngularVelocity * inertiaTensor;
  1062. Activate();
  1063. }
  1064. /*
  1065. ================
  1066. idPhysics_RigidBody::GetLinearVelocity
  1067. ================
  1068. */
  1069. const idVec3 &idPhysics_RigidBody::GetLinearVelocity( int id ) const {
  1070. static idVec3 curLinearVelocity;
  1071. curLinearVelocity = current.i.linearMomentum * inverseMass;
  1072. return curLinearVelocity;
  1073. }
  1074. /*
  1075. ================
  1076. idPhysics_RigidBody::GetAngularVelocity
  1077. ================
  1078. */
  1079. const idVec3 &idPhysics_RigidBody::GetAngularVelocity( int id ) const {
  1080. static idVec3 curAngularVelocity;
  1081. idMat3 inverseWorldInertiaTensor;
  1082. inverseWorldInertiaTensor = current.i.orientation.Transpose() * inverseInertiaTensor * current.i.orientation;
  1083. curAngularVelocity = inverseWorldInertiaTensor * current.i.angularMomentum;
  1084. return curAngularVelocity;
  1085. }
  1086. /*
  1087. ================
  1088. idPhysics_RigidBody::ClipTranslation
  1089. ================
  1090. */
  1091. void idPhysics_RigidBody::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
  1092. if ( model ) {
  1093. gameLocal.clip.TranslationModel( results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
  1094. clipModel, clipModel->GetAxis(), clipMask,
  1095. model->Handle(), model->GetOrigin(), model->GetAxis() );
  1096. }
  1097. else {
  1098. gameLocal.clip.Translation( results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
  1099. clipModel, clipModel->GetAxis(), clipMask, self );
  1100. }
  1101. }
  1102. /*
  1103. ================
  1104. idPhysics_RigidBody::ClipRotation
  1105. ================
  1106. */
  1107. void idPhysics_RigidBody::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
  1108. if ( model ) {
  1109. gameLocal.clip.RotationModel( results, clipModel->GetOrigin(), rotation,
  1110. clipModel, clipModel->GetAxis(), clipMask,
  1111. model->Handle(), model->GetOrigin(), model->GetAxis() );
  1112. }
  1113. else {
  1114. gameLocal.clip.Rotation( results, clipModel->GetOrigin(), rotation,
  1115. clipModel, clipModel->GetAxis(), clipMask, self );
  1116. }
  1117. }
  1118. /*
  1119. ================
  1120. idPhysics_RigidBody::ClipContents
  1121. ================
  1122. */
  1123. int idPhysics_RigidBody::ClipContents( const idClipModel *model ) const {
  1124. if ( model ) {
  1125. return gameLocal.clip.ContentsModel( clipModel->GetOrigin(), clipModel, clipModel->GetAxis(), -1,
  1126. model->Handle(), model->GetOrigin(), model->GetAxis() );
  1127. }
  1128. else {
  1129. return gameLocal.clip.Contents( clipModel->GetOrigin(), clipModel, clipModel->GetAxis(), -1, NULL );
  1130. }
  1131. }
  1132. /*
  1133. ================
  1134. idPhysics_RigidBody::DisableClip
  1135. ================
  1136. */
  1137. void idPhysics_RigidBody::DisableClip() {
  1138. clipModel->Disable();
  1139. }
  1140. /*
  1141. ================
  1142. idPhysics_RigidBody::EnableClip
  1143. ================
  1144. */
  1145. void idPhysics_RigidBody::EnableClip() {
  1146. clipModel->Enable();
  1147. }
  1148. /*
  1149. ================
  1150. idPhysics_RigidBody::UnlinkClip
  1151. ================
  1152. */
  1153. void idPhysics_RigidBody::UnlinkClip() {
  1154. clipModel->Unlink();
  1155. }
  1156. /*
  1157. ================
  1158. idPhysics_RigidBody::LinkClip
  1159. ================
  1160. */
  1161. void idPhysics_RigidBody::LinkClip() {
  1162. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.i.position, current.i.orientation );
  1163. }
  1164. /*
  1165. ================
  1166. idPhysics_RigidBody::EvaluateContacts
  1167. ================
  1168. */
  1169. bool idPhysics_RigidBody::EvaluateContacts() {
  1170. idVec6 dir;
  1171. int num;
  1172. ClearContacts();
  1173. contacts.SetNum( 10 );
  1174. dir.SubVec3(0) = current.i.linearMomentum + current.lastTimeStep * gravityVector * mass;
  1175. dir.SubVec3(1) = current.i.angularMomentum;
  1176. dir.SubVec3(0).Normalize();
  1177. dir.SubVec3(1).Normalize();
  1178. num = gameLocal.clip.Contacts( &contacts[0], 10, clipModel->GetOrigin(),
  1179. dir, CONTACT_EPSILON, clipModel, clipModel->GetAxis(), clipMask, self );
  1180. contacts.SetNum( num );
  1181. AddContactEntitiesForContacts();
  1182. return ( contacts.Num() != 0 );
  1183. }
  1184. /*
  1185. ================
  1186. idPhysics_RigidBody::SetPushed
  1187. ================
  1188. */
  1189. void idPhysics_RigidBody::SetPushed( int deltaTime ) {
  1190. idRotation rotation;
  1191. rotation = ( saved.i.orientation * current.i.orientation ).ToRotation();
  1192. // velocity with which the af is pushed
  1193. current.pushVelocity.SubVec3(0) += ( current.i.position - saved.i.position ) / ( deltaTime * idMath::M_MS2SEC );
  1194. current.pushVelocity.SubVec3(1) += rotation.GetVec() * -DEG2RAD( rotation.GetAngle() ) / ( deltaTime * idMath::M_MS2SEC );
  1195. }
  1196. /*
  1197. ================
  1198. idPhysics_RigidBody::GetPushedLinearVelocity
  1199. ================
  1200. */
  1201. const idVec3 &idPhysics_RigidBody::GetPushedLinearVelocity( const int id ) const {
  1202. return current.pushVelocity.SubVec3(0);
  1203. }
  1204. /*
  1205. ================
  1206. idPhysics_RigidBody::GetPushedAngularVelocity
  1207. ================
  1208. */
  1209. const idVec3 &idPhysics_RigidBody::GetPushedAngularVelocity( const int id ) const {
  1210. return current.pushVelocity.SubVec3(1);
  1211. }
  1212. /*
  1213. ================
  1214. idPhysics_RigidBody::SetMaster
  1215. ================
  1216. */
  1217. void idPhysics_RigidBody::SetMaster( idEntity *master, const bool orientated ) {
  1218. idVec3 masterOrigin;
  1219. idMat3 masterAxis;
  1220. if ( master ) {
  1221. if ( !hasMaster ) {
  1222. // transform from world space to master space
  1223. self->GetMasterPosition( masterOrigin, masterAxis );
  1224. current.localOrigin = ( current.i.position - masterOrigin ) * masterAxis.Transpose();
  1225. if ( orientated ) {
  1226. current.localAxis = current.i.orientation * masterAxis.Transpose();
  1227. }
  1228. else {
  1229. current.localAxis = current.i.orientation;
  1230. }
  1231. hasMaster = true;
  1232. isOrientated = orientated;
  1233. ClearContacts();
  1234. }
  1235. }
  1236. else {
  1237. if ( hasMaster ) {
  1238. hasMaster = false;
  1239. Activate();
  1240. }
  1241. }
  1242. }
  1243. const float RB_VELOCITY_MAX = 16000;
  1244. const int RB_VELOCITY_TOTAL_BITS = 16;
  1245. const int RB_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_VELOCITY_MAX ) ) + 1;
  1246. const int RB_VELOCITY_MANTISSA_BITS = RB_VELOCITY_TOTAL_BITS - 1 - RB_VELOCITY_EXPONENT_BITS;
  1247. const float RB_MOMENTUM_MAX = 1e20f;
  1248. const int RB_MOMENTUM_TOTAL_BITS = 16;
  1249. const int RB_MOMENTUM_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_MOMENTUM_MAX ) ) + 1;
  1250. const int RB_MOMENTUM_MANTISSA_BITS = RB_MOMENTUM_TOTAL_BITS - 1 - RB_MOMENTUM_EXPONENT_BITS;
  1251. const float RB_FORCE_MAX = 1e20f;
  1252. const int RB_FORCE_TOTAL_BITS = 16;
  1253. const int RB_FORCE_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_FORCE_MAX ) ) + 1;
  1254. const int RB_FORCE_MANTISSA_BITS = RB_FORCE_TOTAL_BITS - 1 - RB_FORCE_EXPONENT_BITS;
  1255. /*
  1256. ================
  1257. idPhysics_RigidBody::WriteToSnapshot
  1258. ================
  1259. */
  1260. void idPhysics_RigidBody::WriteToSnapshot( idBitMsg &msg ) const {
  1261. idCQuat quat, localQuat;
  1262. quat = current.i.orientation.ToCQuat();
  1263. msg.WriteFloat( current.i.position[0] );
  1264. msg.WriteFloat( current.i.position[1] );
  1265. msg.WriteFloat( current.i.position[2] );
  1266. msg.WriteFloat( quat.x );
  1267. msg.WriteFloat( quat.y );
  1268. msg.WriteFloat( quat.z );
  1269. msg.WriteFloat( current.i.linearMomentum[0], RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1270. msg.WriteFloat( current.i.linearMomentum[1], RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1271. msg.WriteFloat( current.i.linearMomentum[2], RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1272. }
  1273. /*
  1274. ================
  1275. idPhysics_RigidBody::ReadFromSnapshot
  1276. ================
  1277. */
  1278. void idPhysics_RigidBody::ReadFromSnapshot( const idBitMsg &msg ) {
  1279. idCQuat quat, localQuat;
  1280. previous = next;
  1281. next.i.position[0] = msg.ReadFloat();
  1282. next.i.position[1] = msg.ReadFloat();
  1283. next.i.position[2] = msg.ReadFloat();
  1284. quat.x = msg.ReadFloat();
  1285. quat.y = msg.ReadFloat();
  1286. quat.z = msg.ReadFloat();
  1287. next.i.linearMomentum[0] = msg.ReadFloat( RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1288. next.i.linearMomentum[1] = msg.ReadFloat( RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1289. next.i.linearMomentum[2] = msg.ReadFloat( RB_MOMENTUM_EXPONENT_BITS, RB_MOMENTUM_MANTISSA_BITS );
  1290. next.i.orientation = quat.ToMat3();
  1291. // Make sure to initially set them up. Dont try to interpolate yet.
  1292. if( self->GetNumSnapshotsReceived() <= 1 ) {
  1293. current = next;
  1294. }
  1295. if ( clipModel ) {
  1296. clipModel->Link( gameLocal.clip, self, clipModel->GetId(), next.i.position, next.i.orientation );
  1297. }
  1298. }