#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.DWarning( "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; } if ( groundTrace.c.entityNum >= 0 && groundTrace.c.entityNum < MAX_CLIENTS ) { 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 } }