672 lines
17 KiB
C++
672 lines
17 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 "Moveable.h"
|
||
|
#include "ContentMask.h"
|
||
|
#include "Projectile.h"
|
||
|
#include "Player.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableNetworkData::~sdMoveableNetworkData
|
||
|
================
|
||
|
*/
|
||
|
sdMoveableNetworkData::~sdMoveableNetworkData( void ) {
|
||
|
delete physicsData;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableNetworkData::MakeDefault
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableNetworkData::MakeDefault( void ) {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->MakeDefault();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableNetworkData::Write
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableNetworkData::Write( idFile* file ) const {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->Write( file );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableNetworkData::Read
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableNetworkData::Read( idFile* file ) {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->Read( file );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableBroadcastData::~sdMoveableBroadcastData
|
||
|
================
|
||
|
*/
|
||
|
sdMoveableBroadcastData::~sdMoveableBroadcastData( void ) {
|
||
|
delete physicsData;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableBroadcastData::MakeDefault
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableBroadcastData::MakeDefault( void ) {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->MakeDefault();
|
||
|
}
|
||
|
hidden = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableBroadcastData::Write
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableBroadcastData::Write( idFile* file ) const {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->Write( file );
|
||
|
}
|
||
|
|
||
|
file->WriteBool( hidden );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdMoveableBroadcastData::Read
|
||
|
================
|
||
|
*/
|
||
|
void sdMoveableBroadcastData::Read( idFile* file ) {
|
||
|
if ( physicsData ) {
|
||
|
physicsData->Read( file );
|
||
|
}
|
||
|
|
||
|
file->ReadBool( hidden );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
idMoveable
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
const idEventDef EV_BecomeNonSolid( "becomeNonSolid", '\0', DOC_TEXT( "Makes the object non-solid to players/vehicles, bullets will still collide however." ), 0, NULL );
|
||
|
const idEventDef EV_EnableDamage( "enableDamage", '\0', DOC_TEXT( "Enables/disables doing damage to objects it hits." ), 1, NULL, "b", "value", "Whether to enable/disable." );
|
||
|
|
||
|
CLASS_DECLARATION( idEntity, idMoveable )
|
||
|
EVENT( EV_Activate, idMoveable::Event_Activate )
|
||
|
EVENT( EV_BecomeNonSolid, idMoveable::Event_BecomeNonSolid )
|
||
|
EVENT( EV_EnableDamage, idMoveable::Event_EnableDamage )
|
||
|
END_CLASS
|
||
|
|
||
|
|
||
|
static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
|
||
|
static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::idMoveable
|
||
|
================
|
||
|
*/
|
||
|
idMoveable::idMoveable( void ) {
|
||
|
minDamageVelocity = 100.0f;
|
||
|
maxDamageVelocity = 200.0f;
|
||
|
nextCollideFxTime = 0;
|
||
|
nextDamageTime = 0;
|
||
|
nextSoundTime = 0;
|
||
|
explode = false;
|
||
|
unbindOnDeath = false;
|
||
|
allowStep = false;
|
||
|
canDamage = false;
|
||
|
waterEffects = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::~idMoveable
|
||
|
================
|
||
|
*/
|
||
|
idMoveable::~idMoveable( void ) {
|
||
|
delete waterEffects;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Spawn
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Spawn( void ) {
|
||
|
idTraceModel trm;
|
||
|
float density, friction, bouncyness, linearFriction, angularFriction;
|
||
|
int clipShrink;
|
||
|
|
||
|
|
||
|
const char* clipModelName = GetClipModelName();
|
||
|
if ( !gameLocal.clip.LoadTraceModel( clipModelName, trm ) ) {
|
||
|
gameLocal.Error( "idMoveable '%s': cannot load collision model %s", name.c_str(), clipModelName );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// if the model should be shrunk
|
||
|
clipShrink = spawnArgs.GetInt( "clipshrink" );
|
||
|
if ( clipShrink != 0 ) {
|
||
|
trm.Shrink( clipShrink * CM_CLIP_EPSILON );
|
||
|
}
|
||
|
|
||
|
// get rigid body properties
|
||
|
spawnArgs.GetFloat( "density", "0.5", density );
|
||
|
density = idMath::ClampFloat( 0.001f, 1000.0f, density );
|
||
|
spawnArgs.GetFloat( "friction", "0.05", friction );
|
||
|
friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
|
||
|
spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness );
|
||
|
bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
|
||
|
spawnArgs.GetFloat( "linear_friction", "0.6", linearFriction );
|
||
|
linearFriction = idMath::ClampFloat( 0.0f, 1.0f, linearFriction );
|
||
|
spawnArgs.GetFloat( "angular_friction", "0.6", angularFriction );
|
||
|
angularFriction = idMath::ClampFloat( 0.0f, 1.0f, angularFriction );
|
||
|
explode = spawnArgs.GetBool( "explode" );
|
||
|
unbindOnDeath = spawnArgs.GetBool( "unbindondeath" );
|
||
|
|
||
|
nextCollideFxTime = 0;
|
||
|
|
||
|
fl.takedamage = true;
|
||
|
damageDecl = DAMAGE_FOR_NAME_UNSAFE( spawnArgs.GetString( "dmg_damage", "" ) );
|
||
|
canDamage = spawnArgs.GetBool( "damageWhenActive" ) ? false : true;
|
||
|
minDamageVelocity = spawnArgs.GetFloat( "minDamageVelocity", "100" );
|
||
|
maxDamageVelocity = spawnArgs.GetFloat( "maxDamageVelocity", "200" );
|
||
|
nextDamageTime = 0;
|
||
|
nextSoundTime = 0;
|
||
|
|
||
|
// setup the physics
|
||
|
physicsObj.SetSelf( this );
|
||
|
physicsObj.SetClipModel( new idClipModel( trm, false ), density, 0 );
|
||
|
physicsObj.GetClipModel( 0 )->SetMaterial( GetRenderModelMaterial() );
|
||
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
||
|
physicsObj.SetAxis( GetPhysics()->GetAxis() );
|
||
|
physicsObj.SetBouncyness( bouncyness );
|
||
|
physicsObj.SetFriction( linearFriction, angularFriction, friction );
|
||
|
physicsObj.SetGravity( gameLocal.GetGravity() );
|
||
|
physicsObj.SetContents( CONTENTS_SOLID, 0 );
|
||
|
physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_SLIDEMOVER | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP | CONTENTS_FORCEFIELD, 0 );
|
||
|
physicsObj.SetMass( spawnArgs.GetFloat( "mass", "10" ), 0 );
|
||
|
physicsObj.SetBuoyancy( spawnArgs.GetFloat( "buoyancy", "10" ) );
|
||
|
SetPhysics( &physicsObj );
|
||
|
|
||
|
if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) ) {
|
||
|
physicsObj.DisableImpact();
|
||
|
}
|
||
|
|
||
|
if ( spawnArgs.GetBool( "nonsolid" ) ) {
|
||
|
BecomeNonSolid();
|
||
|
}
|
||
|
|
||
|
allowStep = spawnArgs.GetBool( "allowStep", "1" );
|
||
|
|
||
|
waterEffects = sdWaterEffects::SetupFromSpawnArgs( spawnArgs );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Hide
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Hide( void ) {
|
||
|
idEntity::Hide();
|
||
|
physicsObj.SetContents( 0, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Show
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Show( void ) {
|
||
|
idEntity::Show();
|
||
|
if ( !spawnArgs.GetBool( "nonsolid" ) ) {
|
||
|
physicsObj.SetContents( CONTENTS_SOLID, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
idMoveable::Collide
|
||
|
=================
|
||
|
*/
|
||
|
bool idMoveable::Collide( const trace_t &collision, const idVec3 &velocity, int bodyId ) {
|
||
|
float v, f;
|
||
|
idVec3 dir;
|
||
|
idEntity *ent;
|
||
|
|
||
|
v = -( velocity * collision.c.normal );
|
||
|
if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
|
||
|
f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
|
||
|
if ( StartSound( "snd_bounce", SND_ANY, 0, NULL ) ) {
|
||
|
SetSoundVolume( f );
|
||
|
}
|
||
|
nextSoundTime = gameLocal.time + 500;
|
||
|
}
|
||
|
|
||
|
if ( canDamage && damageDecl && gameLocal.time > nextDamageTime ) {
|
||
|
ent = gameLocal.entities[ collision.c.entityNum ];
|
||
|
if ( ent && v > minDamageVelocity ) {
|
||
|
f = v > maxDamageVelocity ? 1.0f : idMath::Sqrt( v - minDamageVelocity ) * ( 1.0f / idMath::Sqrt( maxDamageVelocity - minDamageVelocity ) );
|
||
|
dir = velocity;
|
||
|
dir.NormalizeFast();
|
||
|
ent->Damage( this, this, dir, damageDecl, f, NULL );
|
||
|
nextDamageTime = gameLocal.time + 1000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idMoveable::Killed
|
||
|
============
|
||
|
*/
|
||
|
void idMoveable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location, const sdDeclDamage* damageDecl ) {
|
||
|
if ( unbindOnDeath ) {
|
||
|
Unbind();
|
||
|
}
|
||
|
|
||
|
const char* brokenModel = spawnArgs.GetString( "model_broken" );
|
||
|
if ( *brokenModel ) {
|
||
|
SetModel( brokenModel );
|
||
|
} else {
|
||
|
if ( explode ) {
|
||
|
PostEventMS( &EV_Remove, 1000 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fl.takedamage = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::AllowStep
|
||
|
================
|
||
|
*/
|
||
|
bool idMoveable::AllowStep( void ) const {
|
||
|
return allowStep;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::BecomeNonSolid
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::BecomeNonSolid( void ) {
|
||
|
// set CONTENTS_RENDERMODEL so bullets still collide with the moveable
|
||
|
physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_RENDERMODEL, 0 );
|
||
|
physicsObj.SetClipMask( MASK_SOLID | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP | CONTENTS_FORCEFIELD, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::EnableDamage
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::EnableDamage( bool enable, float duration ) {
|
||
|
canDamage = enable;
|
||
|
if ( duration ) {
|
||
|
PostEventSec( &EV_EnableDamage, duration, ( !enable ) ? 0.0f : 1.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::GetRenderModelMaterial
|
||
|
================
|
||
|
*/
|
||
|
const idMaterial *idMoveable::GetRenderModelMaterial( void ) const {
|
||
|
if ( renderEntity.customShader ) {
|
||
|
return renderEntity.customShader;
|
||
|
}
|
||
|
if ( renderEntity.hModel && renderEntity.hModel->NumSurfaces() ) {
|
||
|
return renderEntity.hModel->Surface( 0 )->material;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Event_BecomeNonSolid
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Event_BecomeNonSolid( void ) {
|
||
|
BecomeNonSolid();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Event_Activate
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Event_Activate( idEntity *activator ) {
|
||
|
float delay;
|
||
|
idVec3 init_velocity, init_avelocity;
|
||
|
|
||
|
Show();
|
||
|
|
||
|
if ( !spawnArgs.GetInt( "notPushable" ) ) {
|
||
|
physicsObj.EnableImpact();
|
||
|
}
|
||
|
|
||
|
physicsObj.Activate();
|
||
|
|
||
|
spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
|
||
|
spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
|
||
|
|
||
|
delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
|
||
|
if ( delay == 0.0f ) {
|
||
|
physicsObj.SetLinearVelocity( init_velocity );
|
||
|
} else {
|
||
|
PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
|
||
|
}
|
||
|
|
||
|
delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
|
||
|
if ( delay == 0.0f ) {
|
||
|
physicsObj.SetAngularVelocity( init_avelocity );
|
||
|
} else {
|
||
|
PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::Event_EnableDamage
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::Event_EnableDamage( bool enable ) {
|
||
|
canDamage = enable;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::CheckWater
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::CheckWater( const idVec3& waterBodyOrg, const idMat3& waterBodyAxis, idCollisionModel* waterBodyModel ) {
|
||
|
if ( waterEffects ) {
|
||
|
waterEffects->SetOrigin( GetPhysics()->GetOrigin() );
|
||
|
waterEffects->SetAxis( GetPhysics()->GetAxis() );
|
||
|
waterEffects->SetVelocity( GetPhysics()->GetLinearVelocity() );
|
||
|
waterEffects->CheckWater( this, waterBodyOrg, waterBodyAxis, waterBodyModel );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::CheckNetworkStateChanges
|
||
|
================
|
||
|
*/
|
||
|
bool idMoveable::CheckNetworkStateChanges( networkStateMode_t mode, const sdEntityStateNetworkData& baseState ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_BASE( sdMoveableNetworkData );
|
||
|
NET_CHECK_STATE_PHYSICS;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_BASE( sdMoveableBroadcastData );
|
||
|
NET_CHECK_STATE_PHYSICS;
|
||
|
|
||
|
if ( baseData.hidden != fl.hidden ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::WriteNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::WriteNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdMoveableNetworkData );
|
||
|
NET_WRITE_STATE_PHYSICS;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdMoveableBroadcastData );
|
||
|
NET_WRITE_STATE_PHYSICS;
|
||
|
|
||
|
newData.hidden = fl.hidden;
|
||
|
msg.WriteBool( newData.hidden );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::ApplyNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::ApplyNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& newState ) {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_NEW( sdMoveableNetworkData );
|
||
|
NET_APPLY_STATE_PHYSICS;
|
||
|
} else if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_NEW( sdMoveableBroadcastData );
|
||
|
NET_APPLY_STATE_PHYSICS;
|
||
|
|
||
|
if ( newData.hidden ) {
|
||
|
Hide();
|
||
|
} else {
|
||
|
Show();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::ReadNetworkState
|
||
|
================
|
||
|
*/
|
||
|
void idMoveable::ReadNetworkState( networkStateMode_t mode, const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
NET_GET_STATES( sdMoveableNetworkData );
|
||
|
NET_READ_STATE_PHYSICS;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
NET_GET_STATES( sdMoveableBroadcastData );
|
||
|
NET_READ_STATE_PHYSICS;
|
||
|
|
||
|
newData.hidden = msg.ReadBool();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idMoveable::CreateNetworkStructure
|
||
|
================
|
||
|
*/
|
||
|
sdEntityStateNetworkData* idMoveable::CreateNetworkStructure( networkStateMode_t mode ) const {
|
||
|
if ( mode == NSM_VISIBLE ) {
|
||
|
sdMoveableNetworkData* newData = new sdMoveableNetworkData();
|
||
|
|
||
|
newData->physicsData = GetPhysics()->CreateNetworkStructure( mode );
|
||
|
|
||
|
return newData;
|
||
|
}
|
||
|
|
||
|
if ( mode == NSM_BROADCAST ) {
|
||
|
sdMoveableBroadcastData* newData = new sdMoveableBroadcastData();
|
||
|
|
||
|
newData->physicsData = GetPhysics()->CreateNetworkStructure( mode );
|
||
|
|
||
|
return newData;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
idBarrel
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
CLASS_DECLARATION( idMoveable, idBarrel )
|
||
|
END_CLASS
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idBarrel::idBarrel
|
||
|
================
|
||
|
*/
|
||
|
idBarrel::idBarrel() {
|
||
|
radius = 1.0f;
|
||
|
barrelAxis = 0;
|
||
|
lastOrigin.Zero();
|
||
|
lastAxis.Identity();
|
||
|
additionalRotation = 0.0f;
|
||
|
additionalAxis.Identity();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idBarrel::BarrelThink
|
||
|
================
|
||
|
*/
|
||
|
void idBarrel::BarrelThink( void ) {
|
||
|
bool wasAtRest, onGround;
|
||
|
float movedDistance, rotatedDistance, angle;
|
||
|
idVec3 curOrigin, gravityNormal, dir;
|
||
|
idMat3 curAxis, axis;
|
||
|
|
||
|
wasAtRest = IsAtRest();
|
||
|
|
||
|
// run physics
|
||
|
RunPhysics();
|
||
|
|
||
|
// only need to give the visual model an additional rotation if the physics were run
|
||
|
if ( !wasAtRest ) {
|
||
|
|
||
|
// current physics state
|
||
|
onGround = GetPhysics()->HasGroundContacts();
|
||
|
curOrigin = GetPhysics()->GetOrigin();
|
||
|
curAxis = GetPhysics()->GetAxis();
|
||
|
|
||
|
// if the barrel is on the ground
|
||
|
if ( onGround ) {
|
||
|
gravityNormal = GetPhysics()->GetGravityNormal();
|
||
|
|
||
|
dir = curOrigin - lastOrigin;
|
||
|
dir -= gravityNormal * dir * gravityNormal;
|
||
|
movedDistance = dir.LengthSqr();
|
||
|
|
||
|
// if the barrel moved and the barrel is not aligned with the gravity direction
|
||
|
if ( movedDistance > 0.0f && idMath::Fabs( gravityNormal * curAxis[barrelAxis] ) < 0.7f ) {
|
||
|
|
||
|
// barrel movement since last think frame orthogonal to the barrel axis
|
||
|
movedDistance = idMath::Sqrt( movedDistance );
|
||
|
dir *= 1.0f / movedDistance;
|
||
|
movedDistance = ( 1.0f - idMath::Fabs( dir * curAxis[barrelAxis] ) ) * movedDistance;
|
||
|
|
||
|
// get rotation about barrel axis since last think frame
|
||
|
angle = lastAxis[(barrelAxis+1)%3] * curAxis[(barrelAxis+1)%3];
|
||
|
angle = idMath::ACos( angle );
|
||
|
// distance along cylinder hull
|
||
|
rotatedDistance = angle * radius;
|
||
|
|
||
|
// if the barrel moved further than it rotated about it's axis
|
||
|
if ( movedDistance > rotatedDistance ) {
|
||
|
|
||
|
// additional rotation of the visual model to make it look
|
||
|
// like the barrel rolls instead of slides
|
||
|
angle = 180.0f * (movedDistance - rotatedDistance) / (radius * idMath::PI);
|
||
|
if ( gravityNormal.Cross( curAxis[barrelAxis] ) * dir < 0.0f ) {
|
||
|
additionalRotation += angle;
|
||
|
} else {
|
||
|
additionalRotation -= angle;
|
||
|
}
|
||
|
dir = vec3_origin;
|
||
|
dir[barrelAxis] = 1.0f;
|
||
|
additionalAxis = idRotation( vec3_origin, dir, additionalRotation ).ToMat3();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// save state for next think
|
||
|
lastOrigin = curOrigin;
|
||
|
lastAxis = curAxis;
|
||
|
}
|
||
|
|
||
|
Present();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idBarrel::Think
|
||
|
================
|
||
|
*/
|
||
|
void idBarrel::Think( void ) {
|
||
|
BarrelThink();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idBarrel::GetPhysicsToVisualTransform
|
||
|
================
|
||
|
*/
|
||
|
bool idBarrel::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
|
||
|
origin = vec3_origin;
|
||
|
axis = additionalAxis;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idBarrel::Spawn
|
||
|
================
|
||
|
*/
|
||
|
void idBarrel::Spawn( void ) {
|
||
|
const idBounds &bounds = GetPhysics()->GetBounds();
|
||
|
|
||
|
// radius of the barrel cylinder
|
||
|
radius = ( bounds[1][0] - bounds[0][0] ) * 0.5f;
|
||
|
|
||
|
// always a vertical barrel with cylinder axis parallel to the z-axis
|
||
|
barrelAxis = 2;
|
||
|
|
||
|
lastOrigin = GetPhysics()->GetOrigin();
|
||
|
lastAxis = GetPhysics()->GetAxis();
|
||
|
|
||
|
additionalRotation = 0.0f;
|
||
|
additionalAxis.Identity();
|
||
|
}
|