1486 lines
40 KiB
C++
1486 lines
40 KiB
C++
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
#include "../precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#include "Physics_SimpleRigidBody.h"
|
||
|
#include "../Entity.h"
|
||
|
#include "../Player.h"
|
||
|
#include "../ContentMask.h"
|
||
|
#include "../script/Script_Helper.h"
|
||
|
#include "../script/Script_ScriptObject.h"
|
||
|
|
||
|
CLASS_DECLARATION( idPhysics_Base, sdPhysics_SimpleRigidBody )
|
||
|
END_CLASS
|
||
|
|
||
|
const float STOP_SPEED = 10.0f;
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyNetworkState::MakeDefault
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyNetworkState::MakeDefault( void ) {
|
||
|
position = vec3_origin;
|
||
|
orientation.x = 0.f;
|
||
|
orientation.y = 0.f;
|
||
|
orientation.z = 0.f;
|
||
|
linearVelocity = vec3_zero;
|
||
|
angularVelocity = vec3_zero;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyNetworkState::Write
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyNetworkState::Write( idFile* file ) const {
|
||
|
file->WriteVec3( position );
|
||
|
file->WriteCQuat( orientation );
|
||
|
file->WriteVec3( linearVelocity );
|
||
|
file->WriteVec3( angularVelocity );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyNetworkState::Read
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyNetworkState::Read( idFile* file ) {
|
||
|
file->ReadVec3( position );
|
||
|
file->ReadCQuat( orientation );
|
||
|
file->ReadVec3( linearVelocity );
|
||
|
file->ReadVec3( angularVelocity );
|
||
|
|
||
|
position.FixDenormals();
|
||
|
orientation.FixDenormals();
|
||
|
angularVelocity.FixDenormals();
|
||
|
linearVelocity.FixDenormals();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyBroadcastState::MakeDefault
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyBroadcastState::MakeDefault( void ) {
|
||
|
localPosition = vec3_origin;
|
||
|
localOrientation.x = 0.f;
|
||
|
localOrientation.y = 0.f;
|
||
|
localOrientation.z = 0.f;
|
||
|
atRest = -1;
|
||
|
orientedClip = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyBroadcastState::Write
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyBroadcastState::Write( idFile* file ) const {
|
||
|
file->WriteVec3( localPosition );
|
||
|
file->WriteCQuat( localOrientation );
|
||
|
file->WriteInt( atRest );
|
||
|
file->WriteBool( orientedClip );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdSimpleRigidBodyBroadcastState::Read
|
||
|
================
|
||
|
*/
|
||
|
void sdSimpleRigidBodyBroadcastState::Read( idFile* file ) {
|
||
|
file->ReadVec3( localPosition );
|
||
|
file->ReadCQuat( localOrientation );
|
||
|
file->ReadInt( atRest );
|
||
|
file->ReadBool( orientedClip );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::Integrate
|
||
|
|
||
|
Very coarse & naive
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::Integrate( float deltaTime, simpleRigidBodyPState_t &next ) {
|
||
|
|
||
|
next = current;
|
||
|
|
||
|
// apply gravity
|
||
|
next.linearVelocity += deltaTime * gravityVector;
|
||
|
|
||
|
// remove any contribution towards the last normal
|
||
|
float velocityTowardsLast = -lastCollideNormal * next.linearVelocity;
|
||
|
if ( velocityTowardsLast > 0.0f ) {
|
||
|
next.linearVelocity += lastCollideNormal * velocityTowardsLast;
|
||
|
}
|
||
|
|
||
|
next.position = next.position + deltaTime * next.linearVelocity;
|
||
|
|
||
|
// rotation
|
||
|
idVec3 rotAxis = current.angularVelocity;
|
||
|
float angle = RAD2DEG( rotAxis.Normalize() );
|
||
|
if ( angle > 0.00001f ) {
|
||
|
idRotation rotation( vec3_origin, rotAxis, -angle * deltaTime );
|
||
|
next.orientation = current.orientation * rotation.ToMat3();
|
||
|
next.orientation.OrthoNormalizeSelf();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::CollisionResponse
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::CollisionResponse( const trace_t &collision, idVec3 &impulse ) {
|
||
|
|
||
|
idVec3& velocity = current.linearVelocity;
|
||
|
idVec3 hitVelocity = velocity;
|
||
|
|
||
|
float normalVel = velocity * collision.c.normal;
|
||
|
idVec3 tangent = velocity - normalVel * collision.c.normal;
|
||
|
float tangentVel = tangent.Normalize();
|
||
|
if ( tangentVel < idMath::FLT_EPSILON ) {
|
||
|
tangent.Set( 0.0f, 0.0f, 1.0f );
|
||
|
tangentVel = 0.0f;
|
||
|
}
|
||
|
|
||
|
// HACK - scale the bouncyness so that things can't stop on steep walls
|
||
|
float bounceScale = 1.0f;
|
||
|
if ( idMath::Fabs( collision.c.normal.z ) < 0.7071f ) {
|
||
|
float bounceNeeded = Min( 1.0f, -90.0f / normalVel );
|
||
|
if ( normalBouncyness > idMath::FLT_EPSILON ) {
|
||
|
bounceScale = Max( 1.0f, bounceNeeded / normalBouncyness );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// bounce the velocity, always bounce off of noplant surfaces.
|
||
|
if ( collision.c.material != NULL && ( collision.c.material->GetSurfaceFlags() & SURF_NOPLANT ) ) {
|
||
|
normalVel = -normalVel;
|
||
|
} else {
|
||
|
normalVel *= -normalBouncyness * bounceScale;
|
||
|
tangentVel *= tangentialBouncyness * bounceScale;
|
||
|
}
|
||
|
velocity = normalVel*collision.c.normal + tangentVel*tangent;
|
||
|
|
||
|
// check for stopping
|
||
|
if ( collision.c.normal.z > 0.2f && velocity.LengthSqr() < Square( stopSpeed ) ) {
|
||
|
velocity.Zero();
|
||
|
current.angularVelocity.Zero();
|
||
|
} else {
|
||
|
if ( collision.c.normal.z > 0.2f ) {
|
||
|
// small velocity -> zero
|
||
|
velocity.FixDenormals( 0.1f );
|
||
|
} else {
|
||
|
velocity.FixDenormals( 0.000001f );
|
||
|
}
|
||
|
|
||
|
// push out from collision surface
|
||
|
current.position += collision.c.normal * 0.01f;
|
||
|
|
||
|
// reflect the angular velocity about the normal too
|
||
|
current.angularVelocity -= ( ( 1.0f - angularBouncyness ) * current.angularVelocity*collision.c.normal )*collision.c.normal;
|
||
|
current.angularVelocity.FixDenormals( 0.000001f );
|
||
|
}
|
||
|
|
||
|
idEntity* ent = gameLocal.entities[ collision.c.entityNum ];
|
||
|
|
||
|
ent->Hit( collision, hitVelocity, self );
|
||
|
return self->Collide( collision, hitVelocity, -1 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::CheckForCollisions
|
||
|
|
||
|
Check for collisions between the current and next state.
|
||
|
If there is a collision the next state is set to the state at the moment of impact.
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::CheckForCollisions( const float deltaTime, simpleRigidBodyPState_t &next, trace_t &collision ) {
|
||
|
collision.fraction = 1.0f;
|
||
|
|
||
|
// calculate the position of the center of mass at current and next
|
||
|
idVec3 CoMstart = current.position + centerOfMass;
|
||
|
idVec3 CoMend = next.position + centerOfMass;
|
||
|
|
||
|
// if there was a collision
|
||
|
if ( gameLocal.clip.Translation( CLIP_DEBUG_PARMS_ENTINFO( self ) collision, CoMstart, CoMend, centeredClipModel, mat3_identity, clipMask, self ) ) {
|
||
|
// set the next state to the state at the moment of impact
|
||
|
next.position = collision.endpos - centerOfMass;
|
||
|
next.orientation = current.orientation;
|
||
|
next.linearVelocity = current.linearVelocity;
|
||
|
next.angularVelocity = current.angularVelocity;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::TestIfAtRest
|
||
|
|
||
|
Returns true if the body is considered at rest.
|
||
|
Does not catch all cases where the body is at rest but is generally good enough.
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::TestIfAtRest( void ) const {
|
||
|
if ( current.atRest >= 0 ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// when its finished bouncing the collision impulse code manually sets the
|
||
|
// velocities to zero
|
||
|
if ( current.linearVelocity != vec3_origin || current.angularVelocity != vec3_origin ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// needs to be touching the ground
|
||
|
if ( contacts.Num() < 1 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::DropToFloorAndRest
|
||
|
|
||
|
Drops the object straight down to the floor and verifies if the object is at rest on the floor.
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::DropToFloorAndRest( void ) {
|
||
|
idVec3 down;
|
||
|
trace_t tr;
|
||
|
|
||
|
if ( testSolid ) {
|
||
|
|
||
|
testSolid = false;
|
||
|
|
||
|
if ( gameLocal.clip.Contents( CLIP_DEBUG_PARMS_ENTINFO( self ) current.position, clipModel, mat3_identity, clipMask, self ) ) {
|
||
|
gameLocal.DWarning( "rigid body in solid for entity '%s' type '%s' at (%s)",
|
||
|
self->name.c_str(), self->GetType()->classname, current.position.ToString(0) );
|
||
|
Rest();
|
||
|
dropToFloor = false;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// put the body on the floor
|
||
|
down = current.position + gravityNormal * 128.0f;
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS_ENTINFO( self ) tr, current.position, down, clipModel, mat3_identity, clipMask, self );
|
||
|
current.position = tr.endpos;
|
||
|
if ( !orientedClip ) {
|
||
|
clipModel->Link( gameLocal.clip, self, clipModel->GetId(), tr.endpos, mat3_identity );
|
||
|
} else {
|
||
|
clipModel->Link( gameLocal.clip, self, clipModel->GetId(), tr.endpos, current.orientation );
|
||
|
}
|
||
|
|
||
|
// if on the floor already
|
||
|
if ( tr.fraction == 0.0f ) {
|
||
|
// test if we are really at rest
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ENTINFO_ONLY( self ) );
|
||
|
if ( !TestIfAtRest() ) {
|
||
|
gameLocal.DWarning( "rigid body not at rest for entity '%s' type '%s' at (%s)",
|
||
|
self->name.c_str(), self->GetType()->classname, current.position.ToString(0) );
|
||
|
}
|
||
|
Rest();
|
||
|
dropToFloor = false;
|
||
|
} else if ( IsOutsideWorld() ) {
|
||
|
// gameLocal.Warning( "rigid body outside world bounds for entity '%s' type '%s' at (%s)",
|
||
|
// self->name.c_str(), self->GetType()->classname, current.position.ToString(0) );
|
||
|
Rest();
|
||
|
dropToFloor = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::DebugDraw
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::DebugDraw( void ) {
|
||
|
|
||
|
if ( rb_showBodies.GetBool() || ( rb_showActive.GetBool() && current.atRest < 0 ) ) {
|
||
|
clipModel->Draw();
|
||
|
}
|
||
|
|
||
|
if ( rb_showMass.GetBool() ) {
|
||
|
gameRenderWorld->DrawText( va( "\n%1.2f", mass ), current.position, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
|
||
|
}
|
||
|
|
||
|
if ( rb_showVelocity.GetBool() ) {
|
||
|
DrawVelocity( clipModel->GetId(), 0.1f, 4.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::sdPhysics_SimpleRigidBody
|
||
|
================
|
||
|
*/
|
||
|
sdPhysics_SimpleRigidBody::sdPhysics_SimpleRigidBody( void ) {
|
||
|
|
||
|
// set default rigid body properties
|
||
|
SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_SLIDEMOVER );
|
||
|
SetBouncyness( 0.6f );
|
||
|
SetStopSpeed( 40.0f );
|
||
|
SetFriction( 0.6f, 0.6f, 0.0f );
|
||
|
SetWaterFriction( 1.f, 1.f );
|
||
|
clipModel = NULL;
|
||
|
centeredClipModel = new idClipModel();
|
||
|
|
||
|
memset( ¤t, 0, sizeof( current ) );
|
||
|
|
||
|
current.atRest = -1;
|
||
|
current.lastTimeStep = MS2SEC( gameLocal.msec );
|
||
|
|
||
|
current.position.Zero();
|
||
|
current.orientation.Identity();
|
||
|
|
||
|
current.linearVelocity.Zero();
|
||
|
current.angularVelocity.Zero();
|
||
|
lastCollideNormal.Zero();
|
||
|
|
||
|
saved = current;
|
||
|
|
||
|
mass = 1.0f;
|
||
|
inverseMass = 1.0f;
|
||
|
centerOfMass.Zero();
|
||
|
inertiaTensor.Identity();
|
||
|
inverseInertiaTensor.Identity();
|
||
|
SetBuoyancy( 1.f );
|
||
|
waterLevel = 0.0f;
|
||
|
|
||
|
dropToFloor = false;
|
||
|
noImpact = false;
|
||
|
noContact = false;
|
||
|
|
||
|
hasMaster = false;
|
||
|
isOrientated = false;
|
||
|
|
||
|
orientedClip = false;
|
||
|
|
||
|
restFunc = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::~sdPhysics_SimpleRigidBody
|
||
|
================
|
||
|
*/
|
||
|
sdPhysics_SimpleRigidBody::~sdPhysics_SimpleRigidBody( void ) {
|
||
|
gameLocal.clip.DeleteClipModel( clipModel );
|
||
|
gameLocal.clip.DeleteClipModel( centeredClipModel );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetClipModel
|
||
|
================
|
||
|
*/
|
||
|
#define MAX_INERTIA_SCALE 10.0f
|
||
|
|
||
|
void sdPhysics_SimpleRigidBody::SetClipModel( idClipModel *model, const float density, int id, bool freeOld ) {
|
||
|
idMat3 inertiaScale;
|
||
|
|
||
|
assert( self );
|
||
|
assert( model ); // we need a clip model
|
||
|
assert( model->IsTraceModel() ); // and it should be a trace model
|
||
|
assert( density > 0.0f ); // density should be valid
|
||
|
|
||
|
if ( clipModel != NULL && clipModel != model && freeOld ) {
|
||
|
gameLocal.clip.DeleteClipModel( clipModel );
|
||
|
}
|
||
|
clipModel = model;
|
||
|
if ( !orientedClip ) {
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.position, mat3_identity );
|
||
|
} else {
|
||
|
clipModel->Link( gameLocal.clip, self, 0, current.position, current.orientation );
|
||
|
}
|
||
|
|
||
|
// get mass properties from the trace model
|
||
|
clipModel->GetMassProperties( density, mass, centerOfMass, inertiaTensor );
|
||
|
|
||
|
// check whether or not the clip model has valid mass properties
|
||
|
if ( mass < idMath::FLT_EPSILON || FLOAT_IS_NAN( mass ) ) {
|
||
|
gameLocal.Warning( "sdPhysics_SimpleRigidBody::SetClipModel: invalid mass for entity '%s' type '%s'",
|
||
|
self->name.c_str(), self->GetType()->classname );
|
||
|
mass = 1.0f;
|
||
|
centerOfMass.Zero();
|
||
|
}
|
||
|
|
||
|
// check whether or not the inertia tensor is balanced
|
||
|
int minIndex = Min3Index( inertiaTensor[0][0], inertiaTensor[1][1], inertiaTensor[2][2] );
|
||
|
inertiaScale.Identity();
|
||
|
inertiaScale[0][0] = inertiaTensor[0][0] / inertiaTensor[minIndex][minIndex];
|
||
|
inertiaScale[1][1] = inertiaTensor[1][1] / inertiaTensor[minIndex][minIndex];
|
||
|
inertiaScale[2][2] = inertiaTensor[2][2] / inertiaTensor[minIndex][minIndex];
|
||
|
|
||
|
if ( inertiaScale[0][0] > MAX_INERTIA_SCALE || inertiaScale[1][1] > MAX_INERTIA_SCALE || inertiaScale[2][2] > MAX_INERTIA_SCALE ) {
|
||
|
gameLocal.Warning( "idPhysics_RigidBody::SetClipModel: unbalanced inertia tensor for entity '%s' type '%s'",
|
||
|
self->name.c_str(), self->GetType()->classname );
|
||
|
|
||
|
// correct the inertia tensor by replacing it with that of a box of the same bounds as this
|
||
|
idTraceModel trm( clipModel->GetBounds() );
|
||
|
trm.GetMassProperties( density, mass, centerOfMass, inertiaTensor );
|
||
|
}
|
||
|
|
||
|
inverseMass = 1.0f / mass;
|
||
|
inverseInertiaTensor = inertiaTensor.Inverse() * ( 1.0f / 6.0f );
|
||
|
|
||
|
current.linearVelocity.Zero();
|
||
|
current.angularVelocity.Zero();
|
||
|
|
||
|
// set up the centered clip model
|
||
|
idTraceModel tempTrace = *clipModel->GetTraceModel();
|
||
|
tempTrace.Translate( -centerOfMass );
|
||
|
centeredClipModel->LoadTraceModel( tempTrace, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetClipModel
|
||
|
================
|
||
|
*/
|
||
|
idClipModel *sdPhysics_SimpleRigidBody::GetClipModel( int id ) const {
|
||
|
return clipModel;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetNumClipModels
|
||
|
================
|
||
|
*/
|
||
|
int sdPhysics_SimpleRigidBody::GetNumClipModels( void ) const {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetMass
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetMass( float mass, int id ) {
|
||
|
assert( mass > 0.0f );
|
||
|
this->mass = mass;
|
||
|
inverseMass = 1.0f / mass;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetMass
|
||
|
================
|
||
|
*/
|
||
|
float sdPhysics_SimpleRigidBody::GetMass( int id ) const {
|
||
|
return mass;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetWaterFriction
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetWaterFriction( const float linear, const float angular ) {
|
||
|
if ( linear < 0.0f || linear > 1.0f ||
|
||
|
angular < 0.0f || angular > 1.0f ) {
|
||
|
return;
|
||
|
}
|
||
|
linearFrictionWater = linear;
|
||
|
angularFrictionWater = angular;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetBuoyancy
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetBuoyancy( float b ) {
|
||
|
buoyancy = b;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetBouncyness
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetBouncyness( const float b ) {
|
||
|
if ( b < 0.0f || b > 1.0f ) {
|
||
|
return;
|
||
|
}
|
||
|
normalBouncyness = b;
|
||
|
tangentialBouncyness = b;
|
||
|
angularBouncyness = -1.0f;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetBouncyness
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetBouncyness( float normal, float tangential, float angular ) {
|
||
|
normalBouncyness = normal;
|
||
|
tangentialBouncyness = tangential;
|
||
|
angularBouncyness = angular;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetStopSpeed
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetStopSpeed( float _stopSpeed ) {
|
||
|
stopSpeed = _stopSpeed;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::Rest
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::Rest( void ) {
|
||
|
int restTime = current.atRest;
|
||
|
|
||
|
current.atRest = gameLocal.time;
|
||
|
current.linearVelocity.Zero();
|
||
|
current.angularVelocity.Zero();
|
||
|
self->BecomeInactive( TH_PHYSICS );
|
||
|
|
||
|
if ( current.atRest != restTime ) {
|
||
|
sdScriptHelper h1;
|
||
|
self->GetScriptObject()->CallNonBlockingScriptEvent( self->GetScriptObject()->GetFunction( "OnRest" ), h1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::DropToFloor
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::DropToFloor( void ) {
|
||
|
dropToFloor = true;
|
||
|
testSolid = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::NoContact
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::NoContact( void ) {
|
||
|
noContact = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::Activate
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::Activate( void ) {
|
||
|
current.atRest = -1;
|
||
|
self->BecomeActive( TH_PHYSICS );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::PutToRest
|
||
|
|
||
|
put to rest untill something collides with this physics object
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::PutToRest( void ) {
|
||
|
Rest();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::EnableImpact
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::EnableImpact( void ) {
|
||
|
noImpact = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::DisableImpact
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::DisableImpact( void ) {
|
||
|
noImpact = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetContents
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetContents( int contents, int id ) {
|
||
|
if ( clipModel ) {
|
||
|
clipModel->SetContents( contents );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetContents
|
||
|
================
|
||
|
*/
|
||
|
int sdPhysics_SimpleRigidBody::GetContents( int id ) const {
|
||
|
return clipModel->GetContents();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetBounds
|
||
|
================
|
||
|
*/
|
||
|
const idBounds &sdPhysics_SimpleRigidBody::GetBounds( int id ) const {
|
||
|
return clipModel->GetBounds();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetAbsBounds
|
||
|
================
|
||
|
*/
|
||
|
const idBounds &sdPhysics_SimpleRigidBody::GetAbsBounds( int id ) const {
|
||
|
return clipModel->GetAbsBounds();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::Evaluate
|
||
|
|
||
|
Evaluate the impulse based rigid body physics.
|
||
|
When a collision occurs an impulse is applied at the moment of impact but
|
||
|
the remaining time after the collision is ignored.
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::Evaluate( int timeStepMSec, int endTimeMSec ) {
|
||
|
simpleRigidBodyPState_t next;
|
||
|
idAngles angles;
|
||
|
trace_t collision;
|
||
|
idVec3 impulse;
|
||
|
idVec3 oldOrigin, masterOrigin;
|
||
|
idMat3 oldAxis, masterAxis;
|
||
|
float timeStep, minTimeStep;
|
||
|
bool collided, cameToRest = false;
|
||
|
|
||
|
current.angularVelocity.FixDenormals();
|
||
|
current.linearVelocity.FixDenormals();
|
||
|
current.orientation.FixDenormals();
|
||
|
current.position.FixDenormals();
|
||
|
|
||
|
timeStep = MS2SEC( timeStepMSec );
|
||
|
minTimeStep = MS2SEC( 1 );
|
||
|
current.lastTimeStep = timeStep;
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
if ( timeStepMSec <= 0 ) {
|
||
|
return true;
|
||
|
}
|
||
|
oldOrigin = current.position;
|
||
|
oldAxis = current.orientation;
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
current.position = masterOrigin + localOrigin * masterAxis;
|
||
|
if ( isOrientated ) {
|
||
|
current.orientation = localAxis * masterAxis;
|
||
|
} else {
|
||
|
current.orientation = localAxis;
|
||
|
}
|
||
|
LinkClip();
|
||
|
current.linearVelocity = ( current.position - oldOrigin ) / timeStep;
|
||
|
current.angularVelocity = ( current.orientation * oldAxis.Transpose() ).ToAngularVelocity() / timeStep;
|
||
|
|
||
|
return ( current.position != oldOrigin || current.orientation != oldAxis );
|
||
|
}
|
||
|
|
||
|
// if the body is at rest
|
||
|
if ( current.atRest >= 0 || timeStep <= 0.0f ) {
|
||
|
DebugDraw();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// move the rigid body velocity into the frame of a pusher
|
||
|
current.linearVelocity -= current.pushVelocity.SubVec3( 0 );
|
||
|
current.angularVelocity -= current.pushVelocity.SubVec3( 1 );
|
||
|
|
||
|
clipModel->Unlink( gameLocal.clip );
|
||
|
|
||
|
int count = 0;
|
||
|
int maxRepetitions = 2;
|
||
|
|
||
|
do {
|
||
|
|
||
|
CheckWater();
|
||
|
|
||
|
// calculate next position and orientation
|
||
|
Integrate( timeStep, next );
|
||
|
|
||
|
// check for collisions from the current to the next state
|
||
|
collided = CheckForCollisions( timeStep, next, collision );
|
||
|
float timeStepUsed = collision.fraction * timeStep;
|
||
|
|
||
|
// set the new state
|
||
|
current = next;
|
||
|
|
||
|
if ( collided ) {
|
||
|
// apply collision impulse
|
||
|
if ( CollisionResponse( collision, impulse ) ) {
|
||
|
current.atRest = gameLocal.time;
|
||
|
}
|
||
|
lastCollideNormal = collision.c.normal;
|
||
|
} else {
|
||
|
lastCollideNormal.Zero();
|
||
|
}
|
||
|
|
||
|
// update the position of the clip model
|
||
|
LinkClip();
|
||
|
|
||
|
DebugDraw();
|
||
|
|
||
|
if ( !noContact ) {
|
||
|
// get contacts
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ENTINFO_ONLY( self ) );
|
||
|
|
||
|
// check if the body has come to rest
|
||
|
if ( TestIfAtRest() ) {
|
||
|
// put to rest
|
||
|
Rest();
|
||
|
cameToRest = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( current.atRest < 0 ) {
|
||
|
ActivateContactEntities();
|
||
|
}
|
||
|
|
||
|
timeStep -= timeStepUsed;
|
||
|
count++;
|
||
|
} while( count < maxRepetitions && timeStep >= minTimeStep );
|
||
|
|
||
|
// move the rigid body velocity back into the world frame
|
||
|
current.linearVelocity += current.pushVelocity.SubVec3( 0 );
|
||
|
current.angularVelocity += current.pushVelocity.SubVec3( 1 );
|
||
|
current.pushVelocity.Zero();
|
||
|
|
||
|
if ( IsOutsideWorld() ) {
|
||
|
// gameLocal.Warning( "rigid body moved outside world bounds for entity '%s' type '%s' at (%s)",
|
||
|
// self->name.c_str(), self->GetType()->classname, current.position.ToString(0) );
|
||
|
Rest();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::UpdateTime
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::UpdateTime( int endTimeMSec ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetTime
|
||
|
================
|
||
|
*/
|
||
|
int sdPhysics_SimpleRigidBody::GetTime( void ) const {
|
||
|
return gameLocal.time;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetImpactInfo
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::GetImpactInfo( const int id, const idVec3 &point, impactInfo_t *info ) const {
|
||
|
idVec3 linearVelocity, angularVelocity;
|
||
|
idMat3 inverseWorldInertiaTensor;
|
||
|
|
||
|
linearVelocity = current.linearVelocity;
|
||
|
angularVelocity = current.angularVelocity;
|
||
|
|
||
|
info->invMass = inverseMass;
|
||
|
info->invInertiaTensor.Zero();
|
||
|
info->position = point - ( current.position + centerOfMass * current.orientation );
|
||
|
info->velocity = linearVelocity + angularVelocity.Cross( info->position );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ApplyImpulse
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::ApplyImpulse( const int id, const idVec3 &point, const idVec3 &impulse ) {
|
||
|
if ( noImpact ) {
|
||
|
return;
|
||
|
}
|
||
|
if ( hasMaster ) {
|
||
|
self->GetMaster()->GetPhysics()->ApplyImpulse( 0, point, impulse );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
current.linearVelocity += impulse * inverseMass;
|
||
|
const idVec3 momentumDelta = ( point - ( current.position + centerOfMass * current.orientation ) ).Cross( impulse );
|
||
|
const idMat3 inverseWorldInertiaTensor = current.orientation.Transpose() * inverseInertiaTensor * current.orientation;
|
||
|
current.angularVelocity += RAD2DEG( inverseWorldInertiaTensor * momentumDelta );
|
||
|
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::AddForce
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::AddForce( const int id, const idVec3 &point, const idVec3 &force ) {
|
||
|
if ( noImpact ) {
|
||
|
return;
|
||
|
}
|
||
|
if ( hasMaster ) {
|
||
|
self->GetMaster()->GetPhysics()->AddForce( 0, point, force );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idVec3 impulse = force * current.lastTimeStep;
|
||
|
ApplyImpulse( id, point, impulse );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::IsAtRest
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::IsAtRest( void ) const {
|
||
|
return current.atRest >= 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetRestStartTime
|
||
|
================
|
||
|
*/
|
||
|
int sdPhysics_SimpleRigidBody::GetRestStartTime( void ) const {
|
||
|
return current.atRest;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::IsPushable
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::IsPushable( void ) const {
|
||
|
return ( !noImpact && !hasMaster );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SaveState
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SaveState( void ) {
|
||
|
saved = current;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::RestoreState
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::RestoreState( void ) {
|
||
|
current = saved;
|
||
|
|
||
|
LinkClip();
|
||
|
|
||
|
EvaluateContacts( CLIP_DEBUG_PARMS_ENTINFO_ONLY( self ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics::SetOrigin
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetOrigin( const idVec3 &newOrigin, int id ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
current.position = masterOrigin + newOrigin * masterAxis;
|
||
|
localOrigin = newOrigin;
|
||
|
} else {
|
||
|
current.position = newOrigin;
|
||
|
}
|
||
|
|
||
|
LinkClip();
|
||
|
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics::SetAxis
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetAxis( const idMat3 &newAxis, int id ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
if ( isOrientated ) {
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
current.orientation = newAxis * masterAxis;
|
||
|
}
|
||
|
localAxis = newAxis;
|
||
|
} else {
|
||
|
current.orientation = newAxis;
|
||
|
}
|
||
|
|
||
|
LinkClip();
|
||
|
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics::Move
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::Translate( const idVec3 &translation, int id ) {
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
localOrigin += translation;
|
||
|
}
|
||
|
current.position += translation;
|
||
|
|
||
|
LinkClip();
|
||
|
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idPhysics::Rotate
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::Rotate( const idRotation &rotation, int id ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
current.orientation *= rotation.ToMat3();
|
||
|
current.position *= rotation;
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
localAxis *= rotation.ToMat3();
|
||
|
localOrigin = ( current.position - masterOrigin ) * masterAxis.Transpose();
|
||
|
}
|
||
|
|
||
|
LinkClip();
|
||
|
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetOrigin
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &sdPhysics_SimpleRigidBody::GetOrigin( int id ) const {
|
||
|
return current.position;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetAxis
|
||
|
================
|
||
|
*/
|
||
|
const idMat3 &sdPhysics_SimpleRigidBody::GetAxis( int id ) const {
|
||
|
return current.orientation;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetLinearVelocity( const idVec3 &newLinearVelocity, int id ) {
|
||
|
current.linearVelocity = newLinearVelocity;
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetAngularVelocity
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetAngularVelocity( const idVec3 &newAngularVelocity, int id ) {
|
||
|
current.angularVelocity = newAngularVelocity;
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &sdPhysics_SimpleRigidBody::GetLinearVelocity( int id ) const {
|
||
|
return current.linearVelocity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetAngularVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &sdPhysics_SimpleRigidBody::GetAngularVelocity( int id ) const {
|
||
|
return current.angularVelocity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ClipTranslation
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
|
||
|
if ( model ) {
|
||
|
gameLocal.clip.TranslationModel( CLIP_DEBUG_PARMS_ENTINFO( self ) results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
|
||
|
clipModel, mat3_identity, clipMask,
|
||
|
model, model->GetOrigin(), model->GetAxis() );
|
||
|
}
|
||
|
else {
|
||
|
gameLocal.clip.Translation( CLIP_DEBUG_PARMS_ENTINFO( self ) results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
|
||
|
clipModel, mat3_identity, clipMask, self );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ClipRotation
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
|
||
|
// physical presence not affected by rotation
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ClipContents
|
||
|
================
|
||
|
*/
|
||
|
int sdPhysics_SimpleRigidBody::ClipContents( const idClipModel *model ) const {
|
||
|
if ( model ) {
|
||
|
return gameLocal.clip.ContentsModel( CLIP_DEBUG_PARMS_ENTINFO( self ) clipModel->GetOrigin(), clipModel, mat3_identity, -1,
|
||
|
model, model->GetOrigin(), model->GetAxis() );
|
||
|
}
|
||
|
else {
|
||
|
return gameLocal.clip.Contents( CLIP_DEBUG_PARMS_ENTINFO( self ) clipModel->GetOrigin(), clipModel, mat3_identity, -1, NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::UnlinkClip
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::UnlinkClip( void ) {
|
||
|
clipModel->Unlink( gameLocal.clip );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::LinkClip
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::LinkClip( void ) {
|
||
|
if ( !orientedClip ) {
|
||
|
clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.position, mat3_identity );
|
||
|
} else {
|
||
|
clipModel->Link( gameLocal.clip, self, clipModel->GetId(), current.position, current.orientation );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::DisableClip
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::DisableClip( bool activateContacting ) {
|
||
|
if ( activateContacting ) {
|
||
|
WakeEntitiesContacting( self, clipModel );
|
||
|
}
|
||
|
clipModel->Disable();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::EnableClip
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::EnableClip( void ) {
|
||
|
clipModel->Enable();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::EvaluateContacts
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::EvaluateContacts( CLIP_DEBUG_PARMS_DECLARATION_ONLY ) {
|
||
|
idVec3 dir;
|
||
|
int num;
|
||
|
|
||
|
ClearContacts();
|
||
|
|
||
|
contacts.SetNum( 10, false );
|
||
|
|
||
|
dir = current.linearVelocity + current.lastTimeStep * gravityVector;
|
||
|
dir.Normalize();
|
||
|
num = gameLocal.clip.Contacts( CLIP_DEBUG_PARMS_ENTINFO( self ) &contacts[0], 10, clipModel->GetOrigin(),
|
||
|
&dir, CONTACT_EPSILON, clipModel, mat3_identity, clipMask, self );
|
||
|
contacts.SetNum( num, false );
|
||
|
|
||
|
AddContactEntitiesForContacts();
|
||
|
|
||
|
return ( contacts.Num() != 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetPushed
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetPushed( int deltaTime ) {
|
||
|
idRotation rotation;
|
||
|
|
||
|
rotation = ( saved.orientation * current.orientation ).ToRotation();
|
||
|
|
||
|
// velocity with which the af is pushed
|
||
|
current.pushVelocity.SubVec3(0) += ( current.position - saved.position ) / ( deltaTime * idMath::M_MS2SEC );
|
||
|
current.pushVelocity.SubVec3(1) += rotation.GetVec() * -DEG2RAD( rotation.GetAngle() ) / ( deltaTime * idMath::M_MS2SEC );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetPushedLinearVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &sdPhysics_SimpleRigidBody::GetPushedLinearVelocity( const int id ) const {
|
||
|
return current.pushVelocity.SubVec3(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::GetPushedAngularVelocity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &sdPhysics_SimpleRigidBody::GetPushedAngularVelocity( const int id ) const {
|
||
|
return current.pushVelocity.SubVec3(1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::SetMaster
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::SetMaster( idEntity *master, const bool orientated ) {
|
||
|
idVec3 masterOrigin;
|
||
|
idMat3 masterAxis;
|
||
|
|
||
|
if ( master ) {
|
||
|
if ( !hasMaster ) {
|
||
|
// transform from world space to master space
|
||
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
||
|
localOrigin = ( current.position - masterOrigin ) * masterAxis.Transpose();
|
||
|
if ( orientated ) {
|
||
|
localAxis = current.orientation * masterAxis.Transpose();
|
||
|
} else {
|
||
|
localAxis = current.orientation;
|
||
|
}
|
||
|
hasMaster = true;
|
||
|
isOrientated = orientated;
|
||
|
ClearContacts();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
localOrigin = vec3_origin;
|
||
|
localAxis = mat3_identity;
|
||
|
|
||
|
if ( hasMaster ) {
|
||
|
hasMaster = false;
|
||
|
Activate();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::CheckWater
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::CheckWater( void ) {
|
||
|
waterLevel = WATERLEVEL_NONE;
|
||
|
|
||
|
const idBounds& absBounds = GetAbsBounds( -1 );
|
||
|
|
||
|
const idClipModel* waterModel;
|
||
|
idCollisionModel* model;
|
||
|
int count = gameLocal.clip.ClipModelsTouchingBounds( CLIP_DEBUG_PARMS_ENTINFO( self ) absBounds, CONTENTS_WATER, &waterModel, 1, NULL );
|
||
|
if ( !count ) {
|
||
|
self->CheckWaterEffectsOnly();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !waterModel->GetNumCollisionModels() ) {
|
||
|
self->CheckWaterEffectsOnly();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
model = waterModel->GetCollisionModel( 0 );
|
||
|
int numPlanes = model->GetNumBrushPlanes();
|
||
|
if ( !numPlanes ) {
|
||
|
self->CheckWaterEffectsOnly();
|
||
|
return;
|
||
|
}
|
||
|
const idBounds& modelBounds = model->GetBounds();
|
||
|
|
||
|
idBounds worldbb;
|
||
|
worldbb.FromTransformedBounds( GetBounds(), GetOrigin(), GetAxis() );
|
||
|
bool submerged = worldbb.GetMaxs()[2] < (modelBounds.GetMaxs()[2] + waterModel->GetOrigin().z);
|
||
|
|
||
|
if ( submerged ) {
|
||
|
waterLevel = WATERLEVEL_HEAD;
|
||
|
}
|
||
|
|
||
|
self->CheckWater( waterModel->GetOrigin(), waterModel->GetAxis(), model );
|
||
|
}
|
||
|
|
||
|
const float RB_ORIGIN_MAX = 32767;
|
||
|
const int RB_ORIGIN_TOTAL_BITS = 24;
|
||
|
const int RB_ORIGIN_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_ORIGIN_MAX ) ) + 1;
|
||
|
const int RB_ORIGIN_MANTISSA_BITS = RB_ORIGIN_TOTAL_BITS - 1 - RB_ORIGIN_EXPONENT_BITS;
|
||
|
|
||
|
const float RB_LINEAR_VELOCITY_MIN = 0.05f;
|
||
|
const float RB_LINEAR_VELOCITY_MAX = 8192.0f;
|
||
|
const int RB_LINEAR_VELOCITY_TOTAL_BITS = 20;
|
||
|
const int RB_LINEAR_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_LINEAR_VELOCITY_MAX, RB_LINEAR_VELOCITY_MIN ) ) + 1;
|
||
|
const int RB_LINEAR_VELOCITY_MANTISSA_BITS = RB_LINEAR_VELOCITY_TOTAL_BITS - 1 - RB_LINEAR_VELOCITY_EXPONENT_BITS;
|
||
|
|
||
|
const float RB_ANGULAR_VELOCITY_MIN = 0.00001f;
|
||
|
const float RB_ANGULAR_VELOCITY_MAX = idMath::PI * 8.0f;
|
||
|
const int RB_ANGULAR_VELOCITY_TOTAL_BITS = 20;
|
||
|
const int RB_ANGULAR_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( RB_ANGULAR_VELOCITY_MAX, RB_ANGULAR_VELOCITY_MIN ) ) + 1;
|
||
|
const int RB_ANGULAR_VELOCITY_MANTISSA_BITS = RB_ANGULAR_VELOCITY_TOTAL_BITS - 1 - RB_ANGULAR_VELOCITY_EXPONENT_BITS;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::CheckNetworkStateChanges
|
||
|
================
|
||
|
*/
|
||
|
bool sdPhysics_SimpleRigidBody::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_BASE( sdSimpleRigidBodyNetworkState );
|
||
|
|
||
|
if ( baseData.angularVelocity != GetAngularVelocity() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.linearVelocity != GetLinearVelocity() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.orientation != current.orientation.ToCQuat() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.position != current.position ) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_BASE( sdSimpleRigidBodyBroadcastState );
|
||
|
|
||
|
if ( baseData.localPosition != localOrigin ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.localOrientation != localAxis.ToCQuat() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.atRest != current.atRest ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( baseData.orientedClip != orientedClip ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::WriteNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdSimpleRigidBodyNetworkState );
|
||
|
|
||
|
// update state
|
||
|
newData.position = current.position;
|
||
|
newData.orientation = current.orientation.ToCQuat();
|
||
|
newData.linearVelocity = GetLinearVelocity();
|
||
|
newData.angularVelocity = GetAngularVelocity();
|
||
|
|
||
|
// write state
|
||
|
msg.WriteDeltaVector( baseData.position, newData.position, RB_ORIGIN_EXPONENT_BITS, RB_ORIGIN_MANTISSA_BITS );
|
||
|
msg.WriteDeltaCQuat( baseData.orientation, newData.orientation );
|
||
|
msg.WriteDeltaVector( baseData.linearVelocity, newData.linearVelocity, RB_LINEAR_VELOCITY_EXPONENT_BITS, RB_LINEAR_VELOCITY_MANTISSA_BITS );
|
||
|
msg.WriteDeltaVector( baseData.angularVelocity, newData.angularVelocity, RB_ANGULAR_VELOCITY_EXPONENT_BITS, RB_ANGULAR_VELOCITY_MANTISSA_BITS );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdSimpleRigidBodyBroadcastState );
|
||
|
|
||
|
// update state
|
||
|
newData.localPosition = localOrigin;
|
||
|
newData.localOrientation = localAxis.ToCQuat();
|
||
|
newData.atRest = current.atRest;
|
||
|
newData.orientedClip = orientedClip;
|
||
|
|
||
|
// write state
|
||
|
msg.WriteDeltaVector( baseData.localPosition, newData.localPosition );
|
||
|
msg.WriteDeltaCQuat( baseData.localOrientation, newData.localOrientation );
|
||
|
msg.WriteDeltaLong( baseData.atRest, newData.atRest );
|
||
|
msg.WriteBool( newData.orientedClip );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ApplyNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
|
||
|
traceCollection.ForceNextUpdate();
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_NEW( sdSimpleRigidBodyNetworkState );
|
||
|
|
||
|
// update state
|
||
|
current.position = newData.position;
|
||
|
current.orientation = newData.orientation.ToMat3();
|
||
|
if ( !newData.linearVelocity.Compare( current.linearVelocity, idMath::FLT_EPSILON ) ) {
|
||
|
SetLinearVelocity( newData.linearVelocity );
|
||
|
}
|
||
|
if ( !newData.angularVelocity.Compare( current.angularVelocity, idMath::FLT_EPSILON ) ) {
|
||
|
SetAngularVelocity( newData.angularVelocity );
|
||
|
}
|
||
|
|
||
|
if ( clipModel ) {
|
||
|
LinkClip();
|
||
|
}
|
||
|
self->UpdateVisuals();
|
||
|
CheckWater();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_NEW( sdSimpleRigidBodyBroadcastState );
|
||
|
|
||
|
// update state
|
||
|
localOrigin = newData.localPosition;
|
||
|
localAxis = newData.localOrientation.ToMat3();
|
||
|
current.atRest = newData.atRest;
|
||
|
|
||
|
if ( orientedClip != newData.orientedClip ) {
|
||
|
orientedClip = newData.orientedClip;
|
||
|
LinkClip();
|
||
|
}
|
||
|
|
||
|
self->UpdateVisuals();
|
||
|
CheckWater();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::ReadNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void sdPhysics_SimpleRigidBody::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdSimpleRigidBodyNetworkState );
|
||
|
|
||
|
// read state
|
||
|
newData.position = msg.ReadDeltaVector( baseData.position, RB_ORIGIN_EXPONENT_BITS, RB_ORIGIN_MANTISSA_BITS );
|
||
|
newData.orientation = msg.ReadDeltaCQuat( baseData.orientation );
|
||
|
newData.linearVelocity = msg.ReadDeltaVector( baseData.linearVelocity, RB_LINEAR_VELOCITY_EXPONENT_BITS, RB_LINEAR_VELOCITY_MANTISSA_BITS );
|
||
|
newData.angularVelocity = msg.ReadDeltaVector( baseData.angularVelocity, RB_ANGULAR_VELOCITY_EXPONENT_BITS, RB_ANGULAR_VELOCITY_MANTISSA_BITS );
|
||
|
|
||
|
newData.position.FixDenormals();
|
||
|
newData.orientation.FixDenormals();
|
||
|
newData.angularVelocity.FixDenormals();
|
||
|
newData.linearVelocity.FixDenormals();
|
||
|
|
||
|
self->OnNewOriginRead( newData.position );
|
||
|
self->OnNewAxesRead( newData.orientation.ToMat3() );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdSimpleRigidBodyBroadcastState );
|
||
|
|
||
|
// read state
|
||
|
newData.localPosition = msg.ReadDeltaVector( baseData.localPosition );
|
||
|
newData.localOrientation = msg.ReadDeltaCQuat( baseData.localOrientation );
|
||
|
newData.atRest = msg.ReadDeltaLong( baseData.atRest );
|
||
|
newData.orientedClip = msg.ReadBool();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPhysics_SimpleRigidBody::CreateNetworkStructure
|
||
|
================
|
||
|
*/
|
||
|
sdEntityStateNetworkData* sdPhysics_SimpleRigidBody::CreateNetworkStructure( networkStateMode_t mode ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
return new sdSimpleRigidBodyNetworkState();
|
||
|
}
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
return new sdSimpleRigidBodyBroadcastState();
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|