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