502 lines
14 KiB
C++
502 lines
14 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $Workfile: $
|
|
// $Date: $
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include <stdio.h>
|
|
#include <memory.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
typedef unsigned char byte;
|
|
#pragma warning(disable:4244)
|
|
|
|
#include "tier0/dbg.h"
|
|
#include "mathlib/vector.h"
|
|
#include "keyframe.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "rope_shared.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Implementation of keyframe.h interface
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Key Frames
|
|
//-----------------------------------------------------------------------------
|
|
#define HIGHEST_KEYFRAME 3
|
|
#define LOWEST_KEYFRAME -3
|
|
|
|
#define TOTAL_KEYFRAMES (HIGHEST_KEYFRAME - LOWEST_KEYFRAME + 1)
|
|
|
|
//
|
|
|
|
struct KeyFrame_t
|
|
{
|
|
Vector vPos;
|
|
Quaternion qRot;
|
|
};
|
|
|
|
|
|
KeyFrame_t g_KeyFrames[ TOTAL_KEYFRAMES ];
|
|
KeyFrame_t *g_KeyFramePtr = &g_KeyFrames[ -LOWEST_KEYFRAME ]; // points to the middle keyframe, keyframe 0
|
|
|
|
bool Motion_SetKeyAngles( int keyNum, Quaternion &quatAngles )
|
|
{
|
|
if ( keyNum > HIGHEST_KEYFRAME || keyNum < LOWEST_KEYFRAME )
|
|
return false;
|
|
|
|
g_KeyFramePtr[keyNum].qRot = quatAngles;
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// Time Modifier function enumeration & implementation
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
typedef float (*TimeModifierFunc_t)(float);
|
|
|
|
typedef struct
|
|
{
|
|
const char *szName;
|
|
TimeModifierFunc_t pFunc;
|
|
|
|
} TimeModifier_t;
|
|
|
|
float TimeModifierFunc_Linear( float time )
|
|
{
|
|
return time;
|
|
}
|
|
|
|
float TimeModifierFunc_Cosine( float time )
|
|
{
|
|
return ( cos((time+1) * M_PI) * 0.5 ) + 0.5;
|
|
}
|
|
|
|
float TimeModifierFunc_TimeSquared( float time )
|
|
{
|
|
return (time * time);
|
|
}
|
|
|
|
TimeModifier_t g_TimeModifiers[] =
|
|
{
|
|
{ "Linear", TimeModifierFunc_Linear },
|
|
{ "Accel/Deaccel (cosine)", TimeModifierFunc_Cosine },
|
|
{ "Accel (time*time)", TimeModifierFunc_TimeSquared },
|
|
};
|
|
|
|
int Motion_GetNumberOfTimeModifiers( void )
|
|
{
|
|
return ARRAYSIZE(g_TimeModifiers);
|
|
}
|
|
|
|
bool Motion_GetTimeModifierDetails( int timeInterpNum, const char **outName )
|
|
{
|
|
if ( timeInterpNum < 0 || timeInterpNum >= Motion_GetNumberOfTimeModifiers() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !g_TimeModifiers[0].szName || !g_TimeModifiers[0].pFunc )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( outName )
|
|
*outName = g_TimeModifiers[0].szName;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : time -
|
|
// timeModifierFuncNum -
|
|
// *outNewTime -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool Motion_CalculateModifiedTime( float time, int timeModifierFuncNum, float *outNewTime )
|
|
{
|
|
*outNewTime = g_TimeModifiers[timeModifierFuncNum].pFunc( time );
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// Position interpolator function enumeration & implementation
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Linear position interpolator.
|
|
// ------------------------------------------------------------------------------------ //
|
|
|
|
class CPositionInterpolator_Linear : public IPositionInterpolator
|
|
{
|
|
public:
|
|
virtual void Release();
|
|
virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq );
|
|
virtual void SetKeyPosition( int keyNum, Vector const &vPos );
|
|
virtual void InterpolatePosition( float time, Vector &vOut );
|
|
virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; }
|
|
};
|
|
|
|
CPositionInterpolator_Linear g_LinearInterpolator;
|
|
|
|
IPositionInterpolator* GetLinearInterpolator()
|
|
{
|
|
return &g_LinearInterpolator;
|
|
}
|
|
|
|
void CPositionInterpolator_Linear::Release()
|
|
{
|
|
}
|
|
|
|
void CPositionInterpolator_Linear::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq )
|
|
{
|
|
*outName = "Linear";
|
|
*outMinKeyReq = 0;
|
|
*outMaxKeyReq = 1;
|
|
}
|
|
|
|
void CPositionInterpolator_Linear::SetKeyPosition( int keyNum, Vector const &vPos )
|
|
{
|
|
Assert ( keyNum <= HIGHEST_KEYFRAME && keyNum >= LOWEST_KEYFRAME );
|
|
VectorCopy( vPos, g_KeyFramePtr[keyNum].vPos );
|
|
}
|
|
|
|
void CPositionInterpolator_Linear::InterpolatePosition( float time, Vector &vOut )
|
|
{
|
|
VectorLerp( g_KeyFramePtr[0].vPos, g_KeyFramePtr[1].vPos, time, vOut );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Catmull-Rom position interpolator.
|
|
// ------------------------------------------------------------------------------------ //
|
|
|
|
class CPositionInterpolator_CatmullRom : public IPositionInterpolator
|
|
{
|
|
public:
|
|
virtual void Release();
|
|
virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq );
|
|
virtual void SetKeyPosition( int keyNum, Vector const &vPos );
|
|
virtual void InterpolatePosition( float time, Vector &vOut );
|
|
virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; }
|
|
};
|
|
|
|
CPositionInterpolator_CatmullRom g_CatmullRomInterpolator;
|
|
|
|
IPositionInterpolator* GetCatmullRomInterpolator()
|
|
{
|
|
return &g_CatmullRomInterpolator;
|
|
}
|
|
|
|
void CPositionInterpolator_CatmullRom::Release()
|
|
{
|
|
}
|
|
|
|
void CPositionInterpolator_CatmullRom::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq )
|
|
{
|
|
*outName = "Catmull-Rom Spline";
|
|
*outMinKeyReq = -1;
|
|
*outMaxKeyReq = 2;
|
|
}
|
|
|
|
void CPositionInterpolator_CatmullRom::SetKeyPosition( int keyNum, Vector const &vPos )
|
|
{
|
|
Assert ( keyNum <= HIGHEST_KEYFRAME && keyNum >= LOWEST_KEYFRAME );
|
|
VectorCopy( vPos, g_KeyFramePtr[keyNum].vPos );
|
|
}
|
|
|
|
void CPositionInterpolator_CatmullRom::InterpolatePosition( float time, Vector &vOut )
|
|
{
|
|
Catmull_Rom_Spline(
|
|
g_KeyFramePtr[-1].vPos,
|
|
g_KeyFramePtr[0].vPos,
|
|
g_KeyFramePtr[1].vPos,
|
|
g_KeyFramePtr[2].vPos,
|
|
time,
|
|
vOut );
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Rope interpolator.
|
|
// ------------------------------------------------------------------------------------ //
|
|
#include "rope_physics.h"
|
|
|
|
class CRopeDelegate : public CSimplePhysics::IHelper
|
|
{
|
|
public:
|
|
virtual void GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel );
|
|
virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes );
|
|
|
|
|
|
public:
|
|
Vector m_CurEndPoints[2];
|
|
};
|
|
|
|
void CRopeDelegate::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel )
|
|
{
|
|
// Gravity.
|
|
pAccel->Init( 0, 0, -1500 );
|
|
}
|
|
|
|
void CRopeDelegate::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes )
|
|
{
|
|
if( nNodes >= 2 )
|
|
{
|
|
pNodes[0].m_vPos = m_CurEndPoints[0];
|
|
pNodes[nNodes-1].m_vPos = m_CurEndPoints[1];
|
|
}
|
|
}
|
|
|
|
|
|
class CPositionInterpolator_Rope : public IPositionInterpolator
|
|
{
|
|
public:
|
|
CPositionInterpolator_Rope();
|
|
|
|
virtual void Release();
|
|
virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq );
|
|
virtual void SetKeyPosition( int keyNum, Vector const &vPos );
|
|
virtual void InterpolatePosition( float time, Vector &vOut );
|
|
virtual bool ProcessKey( char const *pName, char const *pValue );
|
|
|
|
|
|
private:
|
|
CRopePhysics<10> m_RopePhysics;
|
|
CRopeDelegate m_Delegate;
|
|
|
|
float m_flSlack; // Extra length of rope.
|
|
|
|
bool m_bChange;
|
|
int m_nSegments;
|
|
};
|
|
|
|
IPositionInterpolator* GetRopeInterpolator()
|
|
{
|
|
return new CPositionInterpolator_Rope;
|
|
}
|
|
|
|
|
|
CPositionInterpolator_Rope::CPositionInterpolator_Rope()
|
|
{
|
|
m_flSlack = 0;
|
|
m_bChange = false;
|
|
m_nSegments = 5;
|
|
|
|
for( int i=0; i < 2; i++ )
|
|
m_Delegate.m_CurEndPoints[i] = Vector( 1e24, 1e24, 1e24 );
|
|
}
|
|
|
|
void CPositionInterpolator_Rope::Release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
void CPositionInterpolator_Rope::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq )
|
|
{
|
|
*outName = "Rope";
|
|
*outMinKeyReq = 0;
|
|
*outMinKeyReq = 1;
|
|
}
|
|
|
|
void CPositionInterpolator_Rope::SetKeyPosition( int keyNum, Vector const &vPos )
|
|
{
|
|
if( keyNum == 0 || keyNum == 1 )
|
|
{
|
|
if( vPos != m_Delegate.m_CurEndPoints[keyNum] )
|
|
m_bChange = true;
|
|
|
|
m_Delegate.m_CurEndPoints[keyNum] = vPos;
|
|
}
|
|
}
|
|
|
|
void CPositionInterpolator_Rope::InterpolatePosition( float time, Vector &vOut )
|
|
{
|
|
// Check if we need to resimulate..
|
|
if( m_bChange )
|
|
{
|
|
m_RopePhysics.SetNumNodes( m_nSegments );
|
|
|
|
// Init all the nodes.
|
|
for( int i=0; i < m_RopePhysics.NumNodes(); i++ )
|
|
m_RopePhysics.GetNode(i)->m_vPos = m_RopePhysics.GetNode(i)->m_vPrevPos = m_Delegate.m_CurEndPoints[0];
|
|
|
|
float flDist = (m_Delegate.m_CurEndPoints[0] - m_Delegate.m_CurEndPoints[1]).Length();
|
|
flDist += m_flSlack;
|
|
|
|
m_RopePhysics.Restart();
|
|
m_RopePhysics.SetupSimulation( flDist / (m_RopePhysics.NumNodes() - 1), &m_Delegate );
|
|
|
|
// Run the simulation for a while to let the rope settle down..
|
|
m_RopePhysics.Simulate( 5 );
|
|
|
|
m_bChange = false;
|
|
}
|
|
|
|
// Ok, now we have all the nodes setup..
|
|
float flNode = time * (m_RopePhysics.NumNodes()-1);
|
|
int iNode = (int)( flNode );
|
|
VectorLerp(
|
|
m_RopePhysics.GetNode(iNode)->m_vPredicted,
|
|
m_RopePhysics.GetNode(iNode+1)->m_vPredicted,
|
|
flNode - iNode,
|
|
vOut );
|
|
}
|
|
|
|
bool CPositionInterpolator_Rope::ProcessKey( char const *pName, char const *pValue )
|
|
{
|
|
if( stricmp( pName, "Slack" ) == 0 )
|
|
{
|
|
m_flSlack = atof( pValue ) + ROPESLACK_FUDGEFACTOR;
|
|
m_bChange = true;
|
|
return true;
|
|
}
|
|
else if( stricmp( pName, "Type" ) == 0 )
|
|
{
|
|
int iType = atoi( pValue );
|
|
if( iType == 0 )
|
|
m_nSegments = ROPE_MAX_SEGMENTS;
|
|
else if( iType == 1 )
|
|
m_nSegments = ROPE_TYPE1_NUMSEGMENTS;
|
|
else
|
|
m_nSegments = ROPE_TYPE2_NUMSEGMENTS;
|
|
|
|
m_bChange = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// The global table of all the position interpolators.
|
|
// ------------------------------------------------------------------------------------ //
|
|
|
|
typedef IPositionInterpolator* (*PositionInterpolatorCreateFn)();
|
|
PositionInterpolatorCreateFn g_PositionInterpolatorCreateFns[] =
|
|
{
|
|
GetLinearInterpolator,
|
|
GetCatmullRomInterpolator,
|
|
GetRopeInterpolator
|
|
};
|
|
|
|
int Motion_GetNumberOfPositionInterpolators( void )
|
|
{
|
|
return ARRAYSIZE(g_PositionInterpolatorCreateFns);
|
|
}
|
|
|
|
|
|
IPositionInterpolator* Motion_GetPositionInterpolator( int interpNum )
|
|
{
|
|
Assert( interpNum >= 0 && interpNum < Motion_GetNumberOfPositionInterpolators() );
|
|
return g_PositionInterpolatorCreateFns[clamp( interpNum, 0, Motion_GetNumberOfPositionInterpolators() - 1 )]();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// Rotation interpolator function enumeration & implementation
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
typedef void (*RotationInterpolatorFunc_t)(float time, Quaternion &outRot);
|
|
|
|
typedef struct
|
|
{
|
|
char *szName;
|
|
RotationInterpolatorFunc_t pFunc;
|
|
|
|
// defines the range of keys this interpolator needs to function
|
|
int iMinReqKeyFrame;
|
|
int iMaxReqKeyFrame;
|
|
|
|
} RotationInterpolator_t;
|
|
|
|
void RotationInterpolatorFunc_Linear( float time, Quaternion &outRot )
|
|
{
|
|
// basic 4D spherical linear interpolation
|
|
QuaternionSlerp( g_KeyFramePtr[0].qRot, g_KeyFramePtr[1].qRot, time, outRot );
|
|
}
|
|
|
|
RotationInterpolator_t g_RotationInterpolators[] =
|
|
{
|
|
{ "Linear", RotationInterpolatorFunc_Linear, 0, 1 },
|
|
};
|
|
|
|
int Motion_GetNumberOfRotationInterpolators( void )
|
|
{
|
|
return ARRAYSIZE(g_RotationInterpolators);
|
|
}
|
|
|
|
bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, char **outName, int *outMinKeyReq, int *outMaxKeyReq )
|
|
{
|
|
if ( rotInterpNum < 0 || rotInterpNum >= Motion_GetNumberOfRotationInterpolators() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !g_RotationInterpolators[rotInterpNum].szName || !g_RotationInterpolators[rotInterpNum].pFunc )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( outName )
|
|
*outName = g_RotationInterpolators[rotInterpNum].szName;
|
|
|
|
if ( outMinKeyReq )
|
|
*outMinKeyReq = g_RotationInterpolators[rotInterpNum].iMinReqKeyFrame;
|
|
|
|
if ( outMaxKeyReq )
|
|
*outMaxKeyReq = g_RotationInterpolators[rotInterpNum].iMaxReqKeyFrame;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Interpolates a rotation
|
|
// Time is assumed to have already been modified by the TimeModifyFunc (above)
|
|
// Requires the keyframes be already set
|
|
// Input : time - value from 0..1
|
|
// interpFuncNum -
|
|
// *outQuatRotation - result in quaternion form
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool Motion_InterpolateRotation( float time, int interpFuncNum, Quaternion &outQuatRotation )
|
|
{
|
|
if ( time < 0.0f || time > 1.0f )
|
|
return false;
|
|
|
|
g_RotationInterpolators[interpFuncNum].pFunc( time, outQuatRotation );
|
|
return true;
|
|
}
|