source-engine/game/client/viewangleanim.cpp

387 lines
8.0 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "filesystem.h"
#include "viewangleanim.h"
#include "KeyValues.h"
#include "tier0/memdbgon.h"
extern ConVar cl_pitchdown;
extern ConVar cl_pitchup;
// ConCommands useful for creating view animations
CViewAngleAnimation *g_pTestAnimation = NULL;
// create a view animation object to be used for creating an animation. parameter is flags
CON_COMMAND( viewanim_create, "viewanim_create" )
{
if ( g_pTestAnimation )
{
delete g_pTestAnimation;
g_pTestAnimation = NULL;
}
int flags = 0;
if ( args.ArgC() > 1 )
{
flags = atoi( args[1] );
}
g_pTestAnimation = CREATE_ENTITY( CViewAngleAnimation, "viewangleanim" );
if ( g_pTestAnimation )
{
g_pTestAnimation->Spawn();
}
}
// run the test animation
void TestViewAnim( void )
{
if ( g_pTestAnimation )
{
QAngle angles;
engine->GetViewAngles( angles );
g_pTestAnimation->RunAnimation( angles );
}
else
Msg( "No view anim created\n" );
}
ConCommand viewanim_test( "viewanim_test", TestViewAnim, "test view animation" );
// set view angles to (0,0,0)
void ResetViewAngles( void )
{
// create a blank anim
QAngle angles = vec3_angle;
engine->SetViewAngles( angles );
}
ConCommand viewanim_reset( "viewanim_reset", ResetViewAngles, "reset view angles!", FCVAR_CHEAT );
// add a key frame to the test animation. first parameter is the time taken to get to this keyframe
CON_COMMAND_F( viewanim_addkeyframe, "", FCVAR_CHEAT )
{
if ( g_pTestAnimation )
{
QAngle vecTarget;
engine->GetViewAngles( vecTarget );
float flDelay = 0.2;
if (args.ArgC() > 1)
{
flDelay = atof( args[1] );
}
int iFlags = 0;
if (args.ArgC() > 1)
{
iFlags = atof( args[2] );
}
g_pTestAnimation->AddKeyFrame( new CViewAngleKeyFrame( vecTarget, flDelay, iFlags ) );
}
else
Msg( "No view anim created, use viewanim_create" );
}
// save the current test anim, pass filename
CON_COMMAND( viewanim_save, "Save current animation to file" )
{
if (args.ArgC() < 2)
return;
if ( g_pTestAnimation )
{
g_pTestAnimation->SaveAsAnimFile( args[1] );
}
else
{
Msg( "No view anim created\n" );
}
}
// load a view animation file into the test anim
CON_COMMAND( viewanim_load, "load animation from file" )
{
if (args.ArgC() < 2)
return;
if ( g_pTestAnimation )
{
g_pTestAnimation->LoadViewAnimFile( args[1] );
}
else
Msg( "No view anim created\n" );
}
LINK_ENTITY_TO_CLASS( viewangleanim, CViewAngleAnimation );
CViewAngleAnimation::CViewAngleAnimation()
{
}
CViewAngleAnimation::~CViewAngleAnimation()
{
DeleteKeyFrames();
}
void CViewAngleAnimation::Spawn( void )
{
m_iFlags = 0;
QAngle angles;
engine->GetViewAngles( angles );
/*
if ( m_iFlags & VIEWANIM_RELATIVE )
{
AddKeyFrame( new CViewAngleKeyFrame( vec3_angle, 0.0, 0 ) );
// seed this so we can add keyframes and have them calc the delta properly
m_vecBaseAngles = angles;
}
else
{
AddKeyFrame( new CViewAngleKeyFrame( angles, 0.0, 0 ) );
}
*/
m_bFinished = true; // don't run right away
ClientEntityList().AddNonNetworkableEntity( this );
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
void CViewAngleAnimation::DeleteKeyFrames()
{
int i, c;
c = m_KeyFrames.Count();
for ( i = c - 1; i >= 0 ; --i )
{
delete m_KeyFrames[ i ];
}
m_KeyFrames.RemoveAll();
}
void CViewAngleAnimation::LoadViewAnimFile( const char *pKeyFrameFileName )
{
DeleteKeyFrames();
// load keyvalues from this file and stuff them in as keyframes
KeyValues *pData = new KeyValues( pKeyFrameFileName );
if ( false == pData->LoadFromFile( filesystem, pKeyFrameFileName, "GAME" ) )
{
Warning( "CViewAngleAnimation::LoadViewAnimFile failed to load script %s\n", pKeyFrameFileName );
pData->deleteThis();
return;
}
QAngle angles;
float flTime;
int iFlags;
KeyValues *pKey = pData->GetFirstSubKey();
while ( pKey )
{
// angles
const char *pszAngles = pKey->GetString( "angles", "0 0 0" );
sscanf( pszAngles, "%f %f %f", &angles[0], &angles[1], &angles[2] );
// time
flTime = pKey->GetFloat( "time", 0.001 );
// flags
iFlags = pKey->GetInt( "flags", 0 );
AddKeyFrame( new CViewAngleKeyFrame( angles, flTime, iFlags ) );
pKey = pKey->GetNextKey();
}
pData->deleteThis();
}
void CViewAngleAnimation::SaveAsAnimFile( const char *pKeyFrameFileName )
{
// save all of our keyframes into the file
KeyValues *pData = new KeyValues( pKeyFrameFileName );
pData->SetInt( "flags", m_iFlags );
KeyValues *pKey = new KeyValues( "keyframe" );
int i;
int c = m_KeyFrames.Count();
char buf[64];
for ( i=0;i<c;i++ )
{
pKey = pData->CreateNewKey();
Q_snprintf( buf, sizeof(buf), "%f %f %f",
m_KeyFrames[i]->m_vecAngles[0],
m_KeyFrames[i]->m_vecAngles[1],
m_KeyFrames[i]->m_vecAngles[2] );
pKey->SetString( "angles", buf );
pKey->SetFloat( "time", m_KeyFrames[i]->m_flTime );
pKey->SetInt( "flags", m_KeyFrames[i]->m_iFlags );
}
pData->SaveToFile( filesystem, pKeyFrameFileName, NULL );
pData->deleteThis();
}
void CViewAngleAnimation::AddKeyFrame( CViewAngleKeyFrame *pKeyFrame )
{
pKeyFrame->m_vecAngles -= m_vecBaseAngles;
m_KeyFrames.AddToTail( pKeyFrame );
}
bool CViewAngleAnimation::IsFinished( void )
{
return m_bFinished;
}
void CViewAngleAnimation::RunAnimation( QAngle angles )
{
if ( m_KeyFrames.Count() == 0 )
{
Warning( "CViewAngleAnimation::RunAnimation called on an empty view animation\n" );
return;
}
m_flAnimStartTime = gpGlobals->curtime;
m_bFinished = false;
m_vecBaseAngles = angles;
m_iFlags = m_KeyFrames[0]->m_iFlags;
if ( !( m_iFlags & VIEWANIM_RELATIVE ) )
{
m_KeyFrames[0]->m_vecAngles = angles;
}
}
void CViewAngleAnimation::ClientThink()
{
if ( IsFinished() )
return;
float flCurrentTime = gpGlobals->curtime - m_flAnimStartTime;
if ( flCurrentTime < 0 )
flCurrentTime = 0.001;
// find two nearest points
int i, c;
c = m_KeyFrames.Count();
float flTime = 0;
for ( i=0;i<c;i++ )
{
if ( flTime + m_KeyFrames[i]->m_flTime > flCurrentTime )
{
break;
}
flTime += m_KeyFrames[i]->m_flTime;
}
Assert( i > 0 );
if ( i >= c )
{
if ( i > 0 )
{
// animation complete, set to end point
SetAngles( m_KeyFrames[i-1]->m_vecAngles );
}
if ( m_pAnimCompleteCallback )
{
m_pAnimCompleteCallback();
}
m_bFinished = true;
return;
}
if ( m_KeyFrames[i]->m_iFlags != m_iFlags )
{
if ( ( m_KeyFrames[i]->m_iFlags & VIEWANIM_RELATIVE ) && !( m_iFlags & VIEWANIM_RELATIVE ) )
{
// new relative position is current angles
engine->GetViewAngles( m_vecBaseAngles );
}
// copy the rest over
m_iFlags = m_KeyFrames[i]->m_iFlags;
}
// previous frame is m_KeyFrames[i-1];
// next frame is m_KeyFrames[i];
float flFraction = ( flCurrentTime - flTime ) / ( m_KeyFrames[i]->m_flTime );
Vector v0, v1, v2, v3;
if ( i-2 <= 0 )
{
QAngleToVector( m_KeyFrames[i-1]->m_vecAngles, v0 );
}
else
{
QAngleToVector( m_KeyFrames[i-2]->m_vecAngles, v0 );
}
QAngleToVector( m_KeyFrames[i-1]->m_vecAngles, v1 );
QAngleToVector( m_KeyFrames[i]->m_vecAngles, v2 );
if ( i+1 >= c )
{
QAngleToVector( m_KeyFrames[i]->m_vecAngles, v3 );
}
else
{
QAngleToVector( m_KeyFrames[i+1]->m_vecAngles, v3 );
}
Vector out;
Catmull_Rom_Spline( v0, v1, v2, v3, flFraction, out );
QAngle vecCalculatedAngles;
QAngleToVector( out, vecCalculatedAngles );
SetAngles( vecCalculatedAngles );
}
void CViewAngleAnimation::SetAngles( QAngle vecCalculatedAngles )
{
if ( m_iFlags & VIEWANIM_RELATIVE )
vecCalculatedAngles += m_vecBaseAngles;
QAngle vecViewAngle;
engine->GetViewAngles( vecViewAngle );
if ( !(FBitSet( m_iFlags, VIEWANIM_IGNORE_X ) ) )
vecViewAngle[PITCH] = vecCalculatedAngles[PITCH];
if ( !(FBitSet( m_iFlags, VIEWANIM_IGNORE_Y ) ) )
vecViewAngle[YAW] = vecCalculatedAngles[YAW];
if ( !(FBitSet( m_iFlags, VIEWANIM_IGNORE_Z ) ) )
vecViewAngle[ROLL] = vecCalculatedAngles[ROLL];
// clamp pitch
vecViewAngle[PITCH] = clamp( vecViewAngle[PITCH], -cl_pitchup.GetFloat(), cl_pitchdown.GetFloat() );
engine->SetViewAngles( vecViewAngle );
}