You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
9.7 KiB
313 lines
9.7 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "c_ai_basenpc.h" |
|
#include "engine/ivmodelinfo.h" |
|
#include "rope_physics.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "fx_line.h" |
|
#include "engine/ivdebugoverlay.h" |
|
#include "bone_setup.h" |
|
#include "model_types.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#define BARNACLE_TONGUE_POINTS 7 |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class C_NPC_Barnacle : public C_AI_BaseNPC |
|
{ |
|
public: |
|
|
|
DECLARE_CLASS( C_NPC_Barnacle, C_AI_BaseNPC ); |
|
DECLARE_CLIENTCLASS(); |
|
|
|
C_NPC_Barnacle( void ); |
|
|
|
virtual void GetRenderBounds( Vector &theMins, Vector &theMaxs ) |
|
{ |
|
BaseClass::GetRenderBounds( theMins, theMaxs ); |
|
|
|
// Extend our bounding box downwards the length of the tongue |
|
theMins -= Vector( 0, 0, m_flAltitude ); |
|
} |
|
|
|
// Purpose: Initialize absmin & absmax to the appropriate box |
|
virtual void ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) |
|
{ |
|
// Extend our bounding box downwards the length of the tongue |
|
CollisionProp()->WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs ); |
|
|
|
// We really care about the tongue tip. The altitude is not really relevant. |
|
VectorMin( *pVecWorldMins, m_vecTip, *pVecWorldMins ); |
|
VectorMax( *pVecWorldMaxs, m_vecTip, *pVecWorldMaxs ); |
|
|
|
// pVecWorldMins->z -= m_flAltitude; |
|
} |
|
|
|
void OnDataChanged( DataUpdateType_t updateType ); |
|
void InitTonguePhysics( void ); |
|
void ClientThink( void ); |
|
void StandardBlendingRules( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ); |
|
|
|
void SetVecTip( const float *pPosition ); |
|
void SetAltitude( float flAltitude ); |
|
|
|
// Purpose: |
|
void ComputeVisualTipPoint( Vector *pTip ); |
|
|
|
protected: |
|
Vector m_vecTipPrevious; |
|
Vector m_vecRoot; |
|
Vector m_vecTip; |
|
Vector m_vecTipDrawOffset; |
|
|
|
private: |
|
// Tongue points |
|
float m_flAltitude; |
|
Vector m_vecTonguePoints[BARNACLE_TONGUE_POINTS]; |
|
CRopePhysics<BARNACLE_TONGUE_POINTS> m_TonguePhysics; |
|
|
|
// Tongue physics delegate |
|
class CBarnaclePhysicsDelegate : public CSimplePhysics::IHelper |
|
{ |
|
public: |
|
virtual void GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ); |
|
virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ); |
|
|
|
C_NPC_Barnacle *m_pBarnacle; |
|
}; |
|
friend class CBarnaclePhysicsDelegate; |
|
CBarnaclePhysicsDelegate m_PhysicsDelegate; |
|
|
|
private: |
|
C_NPC_Barnacle( const C_NPC_Barnacle & ); // not defined, not accessible |
|
}; |
|
|
|
static void RecvProxy_VecTip( const CRecvProxyData *pData, void *pStruct, void *pOut ) |
|
{ |
|
((C_NPC_Barnacle*)pStruct)->SetVecTip( pData->m_Value.m_Vector ); |
|
} |
|
|
|
IMPLEMENT_CLIENTCLASS_DT( C_NPC_Barnacle, DT_Barnacle, CNPC_Barnacle ) |
|
RecvPropFloat( RECVINFO( m_flAltitude ) ), |
|
RecvPropVector( RECVINFO( m_vecRoot ) ), |
|
RecvPropVector( RECVINFO( m_vecTip ), 0, RecvProxy_VecTip ), |
|
RecvPropVector( RECVINFO( m_vecTipDrawOffset ) ), |
|
END_RECV_TABLE() |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
C_NPC_Barnacle::C_NPC_Barnacle( void ) |
|
{ |
|
m_PhysicsDelegate.m_pBarnacle = this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::OnDataChanged( DataUpdateType_t updateType ) |
|
{ |
|
BaseClass::OnDataChanged( updateType ); |
|
|
|
if ( updateType == DATA_UPDATE_CREATED ) |
|
{ |
|
InitTonguePhysics(); |
|
|
|
// We want to think every frame. |
|
SetNextClientThink( CLIENT_THINK_ALWAYS ); |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the tongue altitude |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::SetAltitude( float flAltitude ) |
|
{ |
|
m_flAltitude = flAltitude; |
|
} |
|
|
|
void C_NPC_Barnacle::SetVecTip( const float *pPosition ) |
|
{ |
|
Vector vecNewTip; |
|
vecNewTip.Init( pPosition[0], pPosition[1], pPosition[2] ); |
|
if ( vecNewTip != m_vecTip ) |
|
{ |
|
m_vecTip = vecNewTip; |
|
CollisionProp()->MarkSurroundingBoundsDirty(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::InitTonguePhysics( void ) |
|
{ |
|
// Init tongue spline |
|
// First point is at the top |
|
m_TonguePhysics.SetupSimulation( m_flAltitude / (BARNACLE_TONGUE_POINTS-1), &m_PhysicsDelegate ); |
|
m_TonguePhysics.Restart(); |
|
|
|
// Initialize the positions of the nodes. |
|
m_TonguePhysics.GetFirstNode()->m_vPos = m_vecRoot; |
|
m_TonguePhysics.GetFirstNode()->m_vPrevPos = m_TonguePhysics.GetFirstNode()->m_vPos; |
|
float flAltitude = m_flAltitude; |
|
for( int i = 1; i < m_TonguePhysics.NumNodes(); i++ ) |
|
{ |
|
flAltitude *= 0.5; |
|
CSimplePhysics::CNode *pNode = m_TonguePhysics.GetNode( i ); |
|
pNode->m_vPos = m_TonguePhysics.GetNode(i-1)->m_vPos - Vector(0,0,flAltitude); |
|
pNode->m_vPrevPos = pNode->m_vPos; |
|
|
|
// Set the length of the node's spring |
|
//m_TonguePhysics.ResetNodeSpringLength( i-1, flAltitude ); |
|
} |
|
|
|
m_vecTipPrevious = m_vecTip; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::ClientThink( void ) |
|
{ |
|
m_TonguePhysics.Simulate( gpGlobals->frametime ); |
|
|
|
// Set the spring's length to that of the tongue's extension |
|
m_TonguePhysics.ResetSpringLength( m_flAltitude / (BARNACLE_TONGUE_POINTS-1) ); |
|
|
|
// Necessary because ComputeVisualTipPoint depends on m_vecTipPrevious |
|
Vector vecTemp; |
|
ComputeVisualTipPoint( &vecTemp ); |
|
m_vecTipPrevious = vecTemp; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ) |
|
{ |
|
BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask ); |
|
|
|
if ( !hdr ) |
|
return; |
|
|
|
int firstBone = Studio_BoneIndexByName( hdr, "Barnacle.tongue1" ); |
|
|
|
Vector vecPrevRight; |
|
GetVectors( NULL, &vecPrevRight, NULL ); |
|
|
|
Vector vecPrev = pos[Studio_BoneIndexByName( hdr, "Barnacle.base" )]; |
|
Vector vecCurr = vec3_origin; |
|
Vector vecForward; |
|
for ( int i = 0; i <= BARNACLE_TONGUE_POINTS; i++ ) |
|
{ |
|
// We double up the bones at the last node. |
|
if ( i == BARNACLE_TONGUE_POINTS ) |
|
{ |
|
vecCurr = m_TonguePhysics.GetLastNode()->m_vPos; |
|
} |
|
else |
|
{ |
|
vecCurr = m_TonguePhysics.GetNode(i)->m_vPos; |
|
} |
|
|
|
//debugoverlay->AddBoxOverlay( vecCurr, -Vector(2,2,2), Vector(2,2,2), vec3_angle, 0,255,0, 128, 0.1 ); |
|
|
|
// Fill out the positions in local space |
|
VectorITransform( vecCurr, EntityToWorldTransform(), pos[firstBone+i] ); |
|
vecCurr = pos[firstBone+i]; |
|
|
|
// Disallow twist in the tongue visually |
|
// Forward vector has to follow the tongue, right + up have to minimize twist from |
|
// the previous bone |
|
|
|
// Fill out the angles |
|
if ( i != BARNACLE_TONGUE_POINTS ) |
|
{ |
|
vecForward = (vecCurr - vecPrev); |
|
if ( VectorNormalize( vecForward ) < 1e-3 ) |
|
{ |
|
vecForward.Init( 0, 0, 1 ); |
|
} |
|
} |
|
|
|
// Project the previous vecRight into a plane perpendicular to vecForward |
|
// that's the vector closest to what we want... |
|
Vector vecRight, vecUp; |
|
VectorMA( vecPrevRight, -DotProduct( vecPrevRight, vecForward ), vecForward, vecRight ); |
|
VectorNormalize( vecRight ); |
|
CrossProduct( vecForward, vecRight, vecUp ); |
|
|
|
BasisToQuaternion( vecForward, vecRight, vecUp, q[firstBone+i] ); |
|
|
|
vecPrev = vecCurr; |
|
vecPrevRight = vecRight; |
|
} |
|
} |
|
|
|
//=============================================================================================================================== |
|
// BARNACLE TONGUE PHYSICS |
|
//=============================================================================================================================== |
|
#define TONGUE_GRAVITY 0, 0, -1000 |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::CBarnaclePhysicsDelegate::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ) |
|
{ |
|
// Gravity. |
|
pAccel->Init( TONGUE_GRAVITY ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
#define TIP_SNAP_FACTOR 200 |
|
// Todo: this really ought to be SIMD. |
|
void C_NPC_Barnacle::ComputeVisualTipPoint( Vector *pTip ) |
|
{ |
|
float flTipMove = TIP_SNAP_FACTOR * gpGlobals->frametime; |
|
Vector tipIdeal; |
|
VectorAdd(m_vecTip, m_vecTipDrawOffset, tipIdeal); |
|
if ( tipIdeal.DistToSqr( m_vecTipPrevious ) > (flTipMove * flTipMove) ) |
|
{ |
|
// Inch the visual tip toward the actual tip |
|
VectorSubtract( tipIdeal, m_vecTipPrevious, *pTip ); |
|
VectorNormalize( *pTip ); |
|
*pTip *= flTipMove; |
|
*pTip += m_vecTipPrevious; |
|
} |
|
else |
|
{ |
|
*pTip = tipIdeal; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_NPC_Barnacle::CBarnaclePhysicsDelegate::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ) |
|
{ |
|
// Startpoint always stays at the root |
|
pNodes[0].m_vPos = m_pBarnacle->m_vecRoot; |
|
|
|
// Endpoint always stays at the tip |
|
m_pBarnacle->ComputeVisualTipPoint( &pNodes[nNodes-1].m_vPos ); |
|
}
|
|
|