DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 *) &current.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 }