quake4-sdk/source/game/physics/Physics_Particle.cpp
2007-06-15 00:00:00 +00:00

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( &current, 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
}
}