1022 lines
26 KiB
C++
1022 lines
26 KiB
C++
#include "../../idlib/precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "../Game_local.h"
|
|
#include "../Projectile.h"
|
|
|
|
CLASS_DECLARATION( idPhysics_Base, rvPhysics_Particle )
|
|
END_CLASS
|
|
|
|
const float PRT_OVERCLIP = 1.001f;
|
|
const float PRT_BOUNCESTOP = 10.0f;
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::DropToFloorAndRest
|
|
|
|
Drops the object straight down to the floor
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::DropToFloorAndRest( void ) {
|
|
idVec3 down;
|
|
trace_t tr;
|
|
|
|
if ( testSolid ) {
|
|
testSolid = false;
|
|
if ( gameLocal.Contents( self, current.origin, clipModel, clipModel->GetAxis(), clipMask, self ) ) {
|
|
gameLocal.Warning( "entity in solid '%s' type '%s' at (%s)",
|
|
self->name.c_str(), self->GetType()->classname, current.origin.ToString(0) );
|
|
PutToRest();
|
|
dropToFloor = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// put the body on the floor
|
|
down = current.origin + gravityNormal * 128.0f;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
gameLocal.Translation( self, tr, current.origin, down, clipModel, clipModel->GetAxis(), clipMask, self );
|
|
// RAVEN END
|
|
current.origin = tr.endpos;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), tr.endpos, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
|
|
// if on the floor already
|
|
if ( tr.fraction == 0.0f ) {
|
|
PutToRest();
|
|
EvaluateContacts();//Do a final contact check. Items that drop to floor never do this check otherwise
|
|
dropToFloor = false;
|
|
} else if ( IsOutsideWorld() ) {
|
|
gameLocal.Warning( "entity outside world bounds '%s' type '%s' at (%s)",
|
|
self->name.c_str(), self->GetType()->classname, current.origin.ToString(0) );
|
|
PutToRest();
|
|
dropToFloor = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::DebugDraw
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::DebugDraw( void ) {
|
|
|
|
if ( rb_showBodies.GetBool() || ( rb_showActive.GetBool() && current.atRest < 0 ) ) {
|
|
collisionModelManager->DrawModel( clipModel->GetCollisionModel(), clipModel->GetOrigin(), clipModel->GetAxis(), vec3_origin, mat3_identity, 0.0f );
|
|
}
|
|
|
|
if ( rb_showContacts.GetBool() ) {
|
|
int i;
|
|
for ( i = 0; i < contacts.Num(); i ++ ) {
|
|
idVec3 x, y;
|
|
contacts[i].normal.NormalVectors( x, y );
|
|
gameRenderWorld->DebugLine( colorWhite, contacts[i].point, contacts[i].point + 6.0f * contacts[i].normal );
|
|
gameRenderWorld->DebugLine( colorWhite, contacts[i].point - 2.0f * x, contacts[i].point + 2.0f * x );
|
|
gameRenderWorld->DebugLine( colorWhite, contacts[i].point - 2.0f * y, contacts[i].point + 2.0f * y );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::rvPhysics_Particle
|
|
================
|
|
*/
|
|
rvPhysics_Particle::rvPhysics_Particle( void ) {
|
|
SetClipMask( MASK_SOLID );
|
|
SetBouncyness( 0.6f, true );
|
|
clipModel = NULL;
|
|
|
|
memset( ¤t, 0, sizeof( current ) );
|
|
current.atRest = -1;
|
|
current.origin.Zero();
|
|
saved = current;
|
|
|
|
dropToFloor = false;
|
|
testSolid = false;
|
|
hasMaster = false;
|
|
|
|
SetFriction( 0.6f, 0.6f, 0.0f );
|
|
SetBouncyness ( 0.5f, true );
|
|
|
|
gravityNormal.Zero();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::~rvPhysics_Particle
|
|
================
|
|
*/
|
|
rvPhysics_Particle::~rvPhysics_Particle( void ) {
|
|
if ( clipModel ) {
|
|
delete clipModel;
|
|
clipModel = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::Save
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( current.atRest );
|
|
savefile->WriteVec3( current.localOrigin );
|
|
savefile->WriteMat3( current.localAxis );
|
|
savefile->WriteVec3( current.pushVelocity );
|
|
savefile->WriteVec3( current.origin );
|
|
savefile->WriteVec3( current.velocity );
|
|
savefile->WriteBool( current.onGround );
|
|
savefile->WriteBool( current.inWater ); // cnicholson: Added unsaved var
|
|
|
|
savefile->WriteInt( saved.atRest ); // cnicholson: Added unsaved vars
|
|
savefile->WriteVec3( saved.localOrigin );
|
|
savefile->WriteMat3( saved.localAxis );
|
|
savefile->WriteVec3( saved.pushVelocity );
|
|
savefile->WriteVec3( saved.origin );
|
|
savefile->WriteVec3( saved.velocity );
|
|
savefile->WriteBool( saved.onGround );
|
|
savefile->WriteBool( saved.inWater );
|
|
|
|
savefile->WriteFloat( linearFriction );
|
|
savefile->WriteFloat( angularFriction );
|
|
savefile->WriteFloat( contactFriction );
|
|
savefile->WriteFloat( bouncyness );
|
|
savefile->WriteBool ( allowBounce );
|
|
savefile->WriteBounds ( clipModel->GetBounds ( ) ); // cnicholson: Added unsaved var
|
|
|
|
savefile->WriteBool( dropToFloor );
|
|
savefile->WriteBool( testSolid );
|
|
|
|
savefile->WriteBool( hasMaster );
|
|
savefile->WriteBool( isOrientated ); // cnicholson: Added unsaved var
|
|
extraPassEntity.Save( savefile );
|
|
|
|
savefile->WriteClipModel( clipModel );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::Restore
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( current.atRest );
|
|
savefile->ReadVec3( current.localOrigin );
|
|
savefile->ReadMat3( current.localAxis );
|
|
savefile->ReadVec3( current.pushVelocity );
|
|
savefile->ReadVec3( current.origin );
|
|
savefile->ReadVec3( current.velocity );
|
|
savefile->ReadBool( current.onGround );
|
|
savefile->ReadBool( current.inWater ); // cnicholson: Added unsaved var
|
|
|
|
savefile->ReadInt( saved.atRest ); // cnicholson: Added unsaved vars
|
|
savefile->ReadVec3( saved.localOrigin );
|
|
savefile->ReadMat3( saved.localAxis );
|
|
savefile->ReadVec3( saved.pushVelocity );
|
|
savefile->ReadVec3( saved.origin );
|
|
savefile->ReadVec3( saved.velocity );
|
|
savefile->ReadBool( saved.onGround );
|
|
savefile->ReadBool( saved.inWater );
|
|
|
|
savefile->ReadFloat( linearFriction );
|
|
savefile->ReadFloat( angularFriction );
|
|
savefile->ReadFloat( contactFriction );
|
|
savefile->ReadFloat( bouncyness );
|
|
savefile->ReadBool ( allowBounce );
|
|
|
|
idBounds bounds; // cnicholson: Added unrestored var
|
|
delete clipModel;
|
|
savefile->ReadBounds ( bounds );
|
|
clipModel = new idClipModel ( idTraceModel ( bounds ) );
|
|
|
|
savefile->ReadBool( dropToFloor );
|
|
savefile->ReadBool( testSolid );
|
|
|
|
savefile->ReadBool( hasMaster );
|
|
savefile->ReadBool( isOrientated ); // cnicholson: Added unrestored var
|
|
extraPassEntity.Restore( savefile );
|
|
|
|
savefile->ReadClipModel( clipModel );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetClipModel
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetClipModel( idClipModel *model, const float density, int id, bool freeOld ) {
|
|
|
|
assert( self );
|
|
assert( model ); // we need a clip model
|
|
assert( model->IsTraceModel() ); // and it should be a trace model
|
|
|
|
if ( clipModel && clipModel != model && freeOld ) {
|
|
delete clipModel;
|
|
}
|
|
|
|
clipModel = model;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, 0, current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetClipModel
|
|
================
|
|
*/
|
|
idClipModel *rvPhysics_Particle::GetClipModel( int id ) const {
|
|
return clipModel;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetNumClipModels
|
|
================
|
|
*/
|
|
int rvPhysics_Particle::GetNumClipModels( void ) const {
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetBouncyness
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetBouncyness( const float b, bool _allowBounce ) {
|
|
allowBounce = _allowBounce;
|
|
if ( b < 0.0f || b > 1.0f ) {
|
|
return;
|
|
}
|
|
bouncyness = b;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetFriction
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetFriction( const float linear, const float angular, const float contact ) {
|
|
linearFriction = linear;
|
|
angularFriction = angular;
|
|
contactFriction = contact;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::PutToRest
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::PutToRest( void ) {
|
|
current.atRest = gameLocal.time;
|
|
current.velocity.Zero();
|
|
self->BecomeInactive( TH_PHYSICS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::DropToFloor
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::DropToFloor( void ) {
|
|
dropToFloor = true;
|
|
testSolid = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::Activate
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::Activate( void ) {
|
|
current.atRest = -1;
|
|
self->BecomeActive( TH_PHYSICS );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::EvaluateContacts
|
|
================
|
|
*/
|
|
bool rvPhysics_Particle::EvaluateContacts( void ) {
|
|
ClearContacts();
|
|
AddGroundContacts( clipModel );
|
|
|
|
AddContactEntitiesForContacts();
|
|
|
|
return ( contacts.Num() != 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetContents
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetContents( int contents, int id ) {
|
|
clipModel->SetContents( contents );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetContents
|
|
================
|
|
*/
|
|
int rvPhysics_Particle::GetContents( int id ) const {
|
|
return clipModel->GetContents();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetBounds
|
|
================
|
|
*/
|
|
const idBounds &rvPhysics_Particle::GetBounds( int id ) const {
|
|
return clipModel->GetBounds();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetAbsBounds
|
|
================
|
|
*/
|
|
const idBounds &rvPhysics_Particle::GetAbsBounds( int id ) const {
|
|
return clipModel->GetAbsBounds();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::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 rvPhysics_Particle::Evaluate( int timeStepMSec, int endTimeMSec ) {
|
|
particlePState_t next;
|
|
float timeStep;
|
|
float upspeed;
|
|
|
|
timeStep = MS2SEC( timeStepMSec );
|
|
|
|
// if bound to a master
|
|
if ( hasMaster ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
idVec3 oldOrigin;
|
|
|
|
oldOrigin = current.origin;
|
|
|
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
|
current.origin = masterOrigin + current.localOrigin * masterAxis;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, current.localAxis * masterAxis );
|
|
// RAVEN END
|
|
|
|
trace_t tr;
|
|
gameLocal.Translation( self, tr, oldOrigin, current.origin, clipModel, clipModel->GetAxis(), clipMask, self );
|
|
|
|
if ( tr.fraction < 1.0f ) {
|
|
self->Collide ( tr, current.origin - oldOrigin );
|
|
}
|
|
|
|
DebugDraw();
|
|
|
|
return true;
|
|
}
|
|
|
|
// if the body is at rest
|
|
if ( current.atRest >= 0 || timeStep <= 0.0f ) {
|
|
DebugDraw();
|
|
return false;
|
|
}
|
|
|
|
// if putting the body to rest
|
|
if ( dropToFloor ) {
|
|
DropToFloorAndRest();
|
|
return true;
|
|
}
|
|
|
|
clipModel->Unlink();
|
|
|
|
// Determine if currently on the ground
|
|
CheckGround ( );
|
|
|
|
// Determine the current upward velocity
|
|
if ( gravityNormal != vec3_zero ) {
|
|
upspeed = -( current.velocity * gravityNormal );
|
|
} else {
|
|
upspeed = current.velocity.z;
|
|
}
|
|
|
|
// If not on the ground, or moving upwards, or bouncing and moving toward gravity then do a straight
|
|
// forward slide move and gravity.
|
|
if ( !current.onGround || upspeed > 1.0f || (bouncyness > 0.0f && upspeed < -PRT_BOUNCESTOP && !current.inWater) ) {
|
|
// Force ground off when moving upward
|
|
if ( upspeed > 0.0f ) {
|
|
current.onGround = false;
|
|
}
|
|
SlideMove( current.origin, current.velocity, current.velocity * timeStep );
|
|
if ( current.onGround && upspeed < PRT_BOUNCESTOP ) {
|
|
current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal;
|
|
} else {
|
|
current.velocity += (gravityVector * timeStep);
|
|
}
|
|
} else {
|
|
idVec3 delta;
|
|
|
|
// Slow down due to friction
|
|
ApplyFriction ( timeStep );
|
|
|
|
delta = current.velocity * timeStep;
|
|
current.velocity -= ( current.velocity * gravityNormal ) * gravityNormal;
|
|
if ( delta == vec3_origin ) {
|
|
PutToRest( );
|
|
} else {
|
|
SlideMove( current.origin, current.velocity, delta );
|
|
}
|
|
}
|
|
|
|
// update the position of the clip model
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
|
|
DebugDraw();
|
|
|
|
// get all the ground contacts
|
|
EvaluateContacts();
|
|
|
|
current.pushVelocity.Zero();
|
|
|
|
if ( IsOutsideWorld() ) {
|
|
gameLocal.Warning( "clip model outside world bounds for entity '%s' at (%s)", self->name.c_str(), current.origin.ToString(0) );
|
|
PutToRest();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::UpdateTime
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::UpdateTime( int endTimeMSec ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetTime
|
|
================
|
|
*/
|
|
int rvPhysics_Particle::GetTime( void ) const {
|
|
return gameLocal.time;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::IsAtRest
|
|
================
|
|
*/
|
|
bool rvPhysics_Particle::IsAtRest( void ) const {
|
|
return current.atRest >= 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetRestStartTime
|
|
================
|
|
*/
|
|
int rvPhysics_Particle::GetRestStartTime( void ) const {
|
|
return current.atRest;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::IsPushable
|
|
================
|
|
*/
|
|
bool rvPhysics_Particle::IsPushable( void ) const {
|
|
return ( !hasMaster );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SaveState
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SaveState( void ) {
|
|
saved = current;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::RestoreState
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::RestoreState( void ) {
|
|
current = saved;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
EvaluateContacts();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics::SetOrigin
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetOrigin( const idVec3 &newOrigin, int id ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
current.localOrigin = newOrigin;
|
|
if ( hasMaster ) {
|
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
|
current.origin = masterOrigin + newOrigin * masterAxis;
|
|
}
|
|
else {
|
|
current.origin = newOrigin;
|
|
}
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPhysics::SetAxis
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetAxis( const idMat3 &newAxis, int id ) {
|
|
current.localAxis = newAxis;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, 0, clipModel->GetOrigin(), newAxis );
|
|
// RAVEN END
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::Translate
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::Translate( const idVec3 &translation, int id ) {
|
|
|
|
current.localOrigin += translation;
|
|
current.origin += translation;
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::Rotate(
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::Rotate( const idRotation &rotation, int id ) {
|
|
idVec3 masterOrigin;
|
|
idMat3 masterAxis;
|
|
|
|
current.origin *= rotation;
|
|
if ( hasMaster ) {
|
|
self->GetMasterPosition( masterOrigin, masterAxis );
|
|
current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
|
|
}
|
|
else {
|
|
current.localOrigin = current.origin;
|
|
}
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, 0, current.origin, clipModel->GetAxis() * rotation.ToMat3() );
|
|
// RAVEN END
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetOrigin
|
|
================
|
|
*/
|
|
const idVec3 &rvPhysics_Particle::GetOrigin( int id ) const {
|
|
return clipModel->GetOrigin();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetAxis
|
|
================
|
|
*/
|
|
const idMat3 &rvPhysics_Particle::GetAxis( int id ) const {
|
|
if ( !clipModel ) {
|
|
return idPhysics_Base::GetAxis ( id );
|
|
}
|
|
return clipModel->GetAxis();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetLinearVelocity
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetLinearVelocity( const idVec3 &velocity, int id ) {
|
|
current.velocity = velocity;
|
|
Activate();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetLinearVelocity
|
|
================
|
|
*/
|
|
const idVec3 &rvPhysics_Particle::GetLinearVelocity( int id ) const {
|
|
return current.velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::ClipTranslation
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
|
|
if ( model ) {
|
|
gameLocal.TranslationModel( self, results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
|
|
clipModel, clipModel->GetAxis(), clipMask,
|
|
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
|
|
}
|
|
else {
|
|
gameLocal.Translation( self, results, clipModel->GetOrigin(), clipModel->GetOrigin() + translation,
|
|
clipModel, clipModel->GetAxis(), clipMask, self );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::ClipRotation
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
|
|
if ( model ) {
|
|
gameLocal.RotationModel( self, results, clipModel->GetOrigin(), rotation,
|
|
clipModel, clipModel->GetAxis(), clipMask,
|
|
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
|
|
}
|
|
else {
|
|
gameLocal.Rotation( self, results, clipModel->GetOrigin(), rotation,
|
|
clipModel, clipModel->GetAxis(), clipMask, self );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::ClipContents
|
|
================
|
|
*/
|
|
int rvPhysics_Particle::ClipContents( const idClipModel *model ) const {
|
|
if ( model ) {
|
|
return gameLocal.ContentsModel( self, clipModel->GetOrigin(), clipModel, clipModel->GetAxis(), -1,
|
|
model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
|
|
}
|
|
else {
|
|
return gameLocal.Contents( self, clipModel->GetOrigin(), clipModel, clipModel->GetAxis(), -1, NULL );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::DisableClip
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::DisableClip( void ) {
|
|
clipModel->Disable();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::EnableClip
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::EnableClip( void ) {
|
|
clipModel->Enable();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::UnlinkClip
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::UnlinkClip( void ) {
|
|
clipModel->Unlink();
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::LinkClip
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::LinkClip( void ) {
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetPushed
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::SetPushed( int deltaTime ) {
|
|
// velocity with which the particle is pushed
|
|
current.pushVelocity = ( current.origin - saved.origin ) / ( deltaTime * idMath::M_MS2SEC );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::GetPushedLinearVelocity
|
|
================
|
|
*/
|
|
const idVec3 &rvPhysics_Particle::GetPushedLinearVelocity( const int id ) const {
|
|
return current.pushVelocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SetMaster
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::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 );
|
|
current.localOrigin = ( current.origin - masterOrigin ) * masterAxis.Transpose();
|
|
hasMaster = true;
|
|
}
|
|
ClearContacts();
|
|
}
|
|
else {
|
|
if ( hasMaster ) {
|
|
hasMaster = false;
|
|
Activate();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
rvPhysics_Particle::CheckGround
|
|
=====================
|
|
*/
|
|
void rvPhysics_Particle::CheckGround( void ) {
|
|
trace_t groundTrace;
|
|
idVec3 down;
|
|
|
|
if ( gravityNormal == vec3_zero ) {
|
|
current.onGround = false;
|
|
return;
|
|
}
|
|
|
|
down = current.origin + gravityNormal * CONTACT_EPSILON;
|
|
gameLocal.Translation( self, groundTrace, current.origin, down, clipModel, clipModel->GetAxis(), clipMask, self );
|
|
if ( groundTrace.fraction == 1.0f ) {
|
|
current.onGround = false;
|
|
return;
|
|
}
|
|
|
|
if ( ( groundTrace.c.normal * -gravityNormal ) < 0.7f ) {
|
|
current.onGround = false;
|
|
return;
|
|
}
|
|
|
|
current.onGround = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::ApplyFriction
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::ApplyFriction( float timeStep ) {
|
|
idVec3 vel;
|
|
float speed;
|
|
float newspeed;
|
|
|
|
// ignore slope movement, remove all velocity in gravity direction
|
|
vel = current.velocity + (current.velocity * gravityNormal) * gravityNormal;
|
|
|
|
speed = vel.Length();
|
|
if ( speed < 1.0f ) {
|
|
// remove all movement orthogonal to gravity, allows for sinking underwater
|
|
current.velocity = (current.velocity * gravityNormal) * gravityNormal;
|
|
return;
|
|
}
|
|
|
|
// scale the velocity
|
|
if ( current.onGround ) {
|
|
newspeed = speed - ((speed * contactFriction) * timeStep);
|
|
} else {
|
|
newspeed = speed - ((speed * linearFriction) * timeStep);
|
|
}
|
|
|
|
if (newspeed < 0) {
|
|
newspeed = 0;
|
|
}
|
|
|
|
current.velocity *= ( newspeed / speed );
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::SlideMove
|
|
================
|
|
*/
|
|
bool rvPhysics_Particle::SlideMove( idVec3 &start, idVec3 &velocity, const idVec3 &delta ) {
|
|
int i;
|
|
trace_t tr;
|
|
idVec3 move;
|
|
bool collide, rtnValue = false;
|
|
|
|
move = delta;
|
|
for( i = 0; i < 3; i++ ) { // be sure if you change this upper value in the for() to update the exit condition below!!!!!
|
|
gameLocal.Translation( self, tr, start, start + move, clipModel, clipModel->GetAxis(), clipMask, self, extraPassEntity );
|
|
|
|
start = tr.endpos;
|
|
|
|
if ( tr.fraction == 1.0f ) {
|
|
if ( i > 0 ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hitTeleporter = false;
|
|
|
|
// let the entity know about the collision
|
|
collide = self->Collide( tr, current.velocity, hitTeleporter );
|
|
|
|
idEntity* ent;
|
|
ent = gameLocal.entities[tr.c.entityNum];
|
|
assert ( ent );
|
|
|
|
// If we hit water just clip the move for now and keep on going
|
|
if ( ent->GetPhysics()->GetContents() & CONTENTS_WATER ) {
|
|
// Make sure we dont collide with water again
|
|
clipMask &= ~CONTENTS_WATER;
|
|
|
|
current.inWater = true;
|
|
|
|
// Allow the loop to go one more round to push us through the water
|
|
i--;
|
|
|
|
velocity *= 0.4f;
|
|
|
|
move.ProjectOntoPlane( tr.c.normal, PRT_OVERCLIP );
|
|
continue;
|
|
// bounce the projectile
|
|
} else if ( !current.inWater && allowBounce && bouncyness ) {
|
|
if ( !hitTeleporter ) {
|
|
float dot;
|
|
move = tr.endpos;
|
|
dot = DotProduct( velocity, tr.c.normal );
|
|
velocity = ( velocity - ( 2.0f * dot * tr.c.normal ) ) * bouncyness;
|
|
}
|
|
return true;
|
|
//RAVEN BEGIN
|
|
//jshepard: tr.c.material can (did) crash here if null
|
|
} else if ( allowBounce && tr.c.material && (tr.c.material->GetSurfaceFlags ( ) & SURF_BOUNCE) ) {
|
|
//RAVEN END
|
|
float dot;
|
|
move = tr.endpos;
|
|
dot = DotProduct( velocity, tr.c.normal );
|
|
velocity = ( velocity - ( 2.0f * dot * tr.c.normal ) );
|
|
return true;
|
|
}
|
|
// RAVEN BEGIN
|
|
// dluetscher: removed redundant trace calls
|
|
else {
|
|
i = 4;
|
|
rtnValue = true;
|
|
}
|
|
// RAVEN END
|
|
|
|
// clip the movement delta and velocity
|
|
if( collide ) {
|
|
move.ProjectOntoPlane( tr.c.normal, PRT_OVERCLIP );
|
|
velocity.ProjectOntoPlane( tr.c.normal, PRT_OVERCLIP );
|
|
}
|
|
}
|
|
|
|
return rtnValue;
|
|
}
|
|
|
|
const float PRT_VELOCITY_MAX = 16000;
|
|
const int PRT_VELOCITY_TOTAL_BITS = 16;
|
|
const int PRT_VELOCITY_EXPONENT_BITS = idMath::BitsForInteger( idMath::BitsForFloat( PRT_VELOCITY_MAX ) ) + 1;
|
|
const int PRT_VELOCITY_MANTISSA_BITS = PRT_VELOCITY_TOTAL_BITS - 1 - PRT_VELOCITY_EXPONENT_BITS;
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::WriteToSnapshot
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
msg.WriteLong( current.atRest );
|
|
msg.WriteBits ( current.onGround, 1 );
|
|
msg.WriteFloat( current.origin[0] );
|
|
msg.WriteFloat( current.origin[1] );
|
|
msg.WriteFloat( current.origin[2] );
|
|
// msg.WriteFloat( current.velocity[0], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// msg.WriteFloat( current.velocity[1], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// msg.WriteFloat( current.velocity[2], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
msg.WriteDeltaFloat( 0.0f, current.velocity[0] );
|
|
msg.WriteDeltaFloat( 0.0f, current.velocity[1] );
|
|
msg.WriteDeltaFloat( 0.0f, current.velocity[2] );
|
|
// msg.WriteDeltaFloat( 0.0f, current.pushVelocity[0], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// msg.WriteDeltaFloat( 0.0f, current.pushVelocity[1], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// msg.WriteDeltaFloat( 0.0f, current.pushVelocity[2], PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
msg.WriteDeltaFloat( 0.0f, current.pushVelocity[0] );
|
|
msg.WriteDeltaFloat( 0.0f, current.pushVelocity[1] );
|
|
msg.WriteDeltaFloat( 0.0f, current.pushVelocity[2] );
|
|
|
|
// TODO: Check that this conditional write to delta message is OK
|
|
if ( hasMaster ) {
|
|
idCQuat localQuat;
|
|
localQuat = current.localAxis.ToCQuat();
|
|
|
|
msg.WriteBits ( 1, 1 );
|
|
msg.WriteFloat( localQuat.x );
|
|
msg.WriteFloat( localQuat.y );
|
|
msg.WriteFloat( localQuat.z );
|
|
msg.WriteDeltaFloat( current.origin[0], current.localOrigin[0] );
|
|
msg.WriteDeltaFloat( current.origin[1], current.localOrigin[1] );
|
|
msg.WriteDeltaFloat( current.origin[2], current.localOrigin[2] );
|
|
} else {
|
|
msg.WriteBits ( 0, 1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
rvPhysics_Particle::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void rvPhysics_Particle::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
current.atRest = msg.ReadLong();
|
|
current.onGround = ( msg.ReadBits( 1 ) != 0 );
|
|
current.origin[0] = msg.ReadFloat();
|
|
current.origin[1] = msg.ReadFloat();
|
|
current.origin[2] = msg.ReadFloat();
|
|
// current.velocity[0] = msg.ReadFloat( PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// current.velocity[1] = msg.ReadFloat( PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// current.velocity[2] = msg.ReadFloat( PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
current.velocity[0] = msg.ReadDeltaFloat( 0.0f );
|
|
current.velocity[1] = msg.ReadDeltaFloat( 0.0f );
|
|
current.velocity[2] = msg.ReadDeltaFloat( 0.0f );
|
|
// current.pushVelocity[0] = msg.ReadDeltaFloat( 0.0f, PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// current.pushVelocity[1] = msg.ReadDeltaFloat( 0.0f, PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
// current.pushVelocity[2] = msg.ReadDeltaFloat( 0.0f, PRT_VELOCITY_EXPONENT_BITS, PRT_VELOCITY_MANTISSA_BITS );
|
|
current.pushVelocity[0] = msg.ReadDeltaFloat( 0.0f );
|
|
current.pushVelocity[1] = msg.ReadDeltaFloat( 0.0f );
|
|
current.pushVelocity[2] = msg.ReadDeltaFloat( 0.0f );
|
|
|
|
if ( msg.ReadBits ( 1 ) ) {
|
|
idCQuat localQuat;
|
|
localQuat.x = msg.ReadFloat( );
|
|
localQuat.y = msg.ReadFloat( );
|
|
localQuat.z = msg.ReadFloat( );
|
|
current.localOrigin[0] = msg.ReadDeltaFloat( current.origin[0] );
|
|
current.localOrigin[1] = msg.ReadDeltaFloat( current.origin[1] );
|
|
current.localOrigin[2] = msg.ReadDeltaFloat( current.origin[2] );
|
|
current.localAxis = localQuat.ToMat3();
|
|
}
|
|
|
|
if ( clipModel ) {
|
|
// RAVEN BEGIN
|
|
// ddynerman: multiple clip worlds
|
|
clipModel->Link( self, clipModel->GetId(), current.origin, clipModel->GetAxis() );
|
|
// RAVEN END
|
|
}
|
|
}
|