Пример #1
0
void DisplayWeaponModelPieces( LTObjRef hObject, HWEAPON hWeapon, const char* ModelPieceListAtt, bool bShow, bool bUseAIWeapons )
{
	if( !hWeapon || !hObject )
		return;

	// get the model interface
	ILTModel *pModelLT = g_pLTServer->GetModelLT();
	ASSERT( 0 != pModelLT );

	HWEAPONDATA hWpnData = g_pWeaponDB->GetWeaponData(hWeapon, bUseAIWeapons);

	uint32 NumPieces = g_pWeaponDB->GetNumValues(hWpnData,ModelPieceListAtt);
	for (uint32 PieceIndex=0; PieceIndex < NumPieces; PieceIndex++)
	{
		const char* pszPiece = g_pWeaponDB->GetString(hWpnData, ModelPieceListAtt, PieceIndex);

		HMODELPIECE hPiece = 0;
		if( LT_OK == pModelLT->GetPiece( hObject, pszPiece, hPiece ) )
		{
			// show or hide the model piece
			LTRESULT ltResult = pModelLT->SetPieceHideStatus( hObject, hPiece, !bShow );
			ASSERT( ( LT_OK == ltResult) || ( LT_NOCHANGE == ltResult ) );
		}
	}
}
Пример #2
0
// ----------------------------------------------------------------------- //
//
//	ROUTINE:	CAutoTargetMgr::GenerateNodeArray()
//
//	PURPOSE:	Find the nodes closest to the center of view
//
// ----------------------------------------------------------------------- //
void CAutoTargetMgr::GenerateNodeArray()
{
	int cNodes;

	ILTModel *pModelLT = g_pLTClient->GetModelLT();

	//clear our node array
	m_nNodeCount = 0;


	//step through the chars we know about
	CharFXArray::iterator iter = m_Targets.begin();
	while (iter != m_Targets.end())
	{
		CCharacterFX* pChar = (CCharacterFX*)(*iter);
		ModelsDB::HSKELETON hModelSkeleton = pChar->GetModelSkeleton();
		cNodes = g_pModelsDB->GetSkeletonNumNodes(hModelSkeleton);
	
		// Enumerate through the nodez
		for(int iNode = 0; iNode < cNodes && m_nNodeCount < MAX_AUTOTARGET_NODES; iNode++)
		{
			ModelsDB::HNODE hCurNode = g_pModelsDB->GetSkeletonNode( hModelSkeleton, iNode );
			if( g_pModelsDB->GetNodeAutoTarget( hCurNode ))
			{
				// get the nodes position
				LTTransform lTrans;
				HMODELNODE hNode;

				char const* szNodeName = g_pModelsDB->GetNodeName( hCurNode );
				if( LT_OK == pModelLT->GetNode( pChar->GetServerObj(), szNodeName, hNode ) )
				{
					if( LT_OK == pModelLT->GetNodeTransform( pChar->GetServerObj(), hNode, lTrans, true ) )
					{	
						m_NodeArray[m_nNodeCount].vPos = lTrans.m_vPos;
						m_NodeArray[m_nNodeCount].hChar = pChar->GetServerObj();
						m_nNodeCount++;
					}
				}
			}
		}
		iter++;
	}

}
Пример #3
0
bool CLightningFX::Init(ILTClient *pClientDE, FX_BASEDATA *pBaseData, const CBaseFXProps *pProps)
{
	LTVector vSave = pBaseData->m_vPos;

	// Perform base class initialisation

	if (!CBaseFX::Init(pClientDE, pBaseData, pProps)) 
		return false;

	ObjectCreateStruct ocs;
	INIT_OBJECTCREATESTRUCT(ocs);

	ocs.m_ObjectType		= OT_NORMAL;
	ocs.m_Flags				= pBaseData->m_dwObjectFlags | FLAG_NOLIGHT;
	ocs.m_Flags2			|= pBaseData->m_dwObjectFlags2;
	ocs.m_Pos				= m_vCreatePos;

	m_hObject = m_pLTClient->CreateObject(&ocs);
	if( !m_hObject )
		return false;

	// Are we rendering really close?

	m_bReallyClose = !!(pBaseData->m_dwObjectFlags & FLAG_REALLYCLOSE);

	
	// Create the max number of bolts 

	CLightningBolt *pBolt = LTNULL;
	PT_TRAIL_SECTION ts;

	for( uint32 nBolts = 0; nBolts < GetProps()->m_nMaxNumBolts; ++nBolts )
	{
		pBolt = debug_new( CLightningBolt );

		pBolt->m_nNumSegments = GetRandom( (int)GetProps()->m_nMinSegmentsPerBolt, (int)GetProps()->m_nMaxSegmentsPerBolt );

		// Add all the trail sections now since we don't need to constantly create and delete them...

		for( uint32 nSegs = 0; nSegs < pBolt->m_nNumSegments; ++nSegs )
		{
			ts.m_vPos = m_vCreatePos;
			pBolt->m_collPathPts.AddTail( ts );
		}

		m_lstBolts.push_back( pBolt );
	}
	

	// Setup the target data so we now where the lightning is going...

	if( pBaseData->m_bUseTargetData )
	{
		if( pBaseData->m_hTarget )
		{
			m_hTarget = pBaseData->m_hTarget;
		}
		else if( m_hParent )
		{
			m_hTarget = m_hParent;
		}
		else
		{
			m_hTarget = LTNULL;
		}
		
		m_vTargetPos = pBaseData->m_vTargetPos;
	}
	else
	{
		// Use our parent as the target if we have one otherwise just use ourselves...
		
		m_hTarget = (m_hParent ? m_hParent : m_hObject);
		m_vTargetPos = m_vCreatePos;
	}

	// Load the texture if one was specified...
	
	if( !m_hTexture && GetProps()->m_szTexture[0] )
	{
		m_pLTClient->GetTexInterface()->CreateTextureFromName( m_hTexture, GetProps()->m_szTexture );
	}

	// Create a list of attractor nodes 

	if( m_hTarget )
	{
		ILTModel		*pModelLT = m_pLTClient->GetModelLT();
		ILTCommon		*pCommonLT = m_pLTClient->Common();
		HMODELNODE		hNode = -1;
		HMODELSOCKET	hSocket = -1;
		HATTRACTOR		hAttractor = INVALID_ATTRACTOR;
		CAttractor		cAttractor;

		// Add any nodes to our attractor list...

		if( GetProps()->m_szNodeAttractors[0] )
		{
			ConParse parse( GetProps()->m_szNodeAttractors );
			while( pCommonLT->Parse( &parse ) == LT_OK )
			{
				if( parse.m_nArgs > 0 && parse.m_Args[0] )
				{
					if( pModelLT->GetNode( m_hTarget, parse.m_Args[0], hAttractor ) == LT_OK )
					{
						cAttractor.m_hModel		= m_hTarget;
						cAttractor.m_hAttractor	= hAttractor;
						cAttractor.m_eType		= CAttractor::eNode;
					
						m_lstAttractors.push_back( cAttractor );
					}
				}
			}
		}

		// Add any sockets to our attractor list...

		if( GetProps()->m_szSocketAttractors[0] )
		{
			ConParse parse( GetProps()->m_szSocketAttractors );
			while( pCommonLT->Parse( &parse ) == LT_OK )
			{
				if( parse.m_nArgs > 0 && parse.m_Args[0] )
				{
					if( pModelLT->GetSocket( m_hTarget, parse.m_Args[0], hAttractor ) == LT_OK )
					{
						cAttractor.m_hModel		= m_hTarget;
						cAttractor.m_hAttractor = hAttractor;
						cAttractor.m_eType		= CAttractor::eSocket;
					
						m_lstAttractors.push_back( cAttractor );
					}
				}
			}
		}
	}

	m_tmElapsedEmission = 0.0f;
	m_fDelay = 0.0f;

	return true;
}
Пример #4
0
bool CLTBModelFX::Update(float tmFrameTime)
{

	//Ok, what we are going to do is see if we are supposed to be sync'd to the
	//animation. If so, we are going to flat out ignore tmCur, and instead generate
	//our own. This way we can match the model exactly.
	if(GetProps()->m_bSyncToModelAnim)
	{
		//we need to find out where in the animation the model currently is
		ILTModel		*pLTModel = m_pLTClient->GetModelLT();
		ANIMTRACKERID	nTracker;
		
		if(pLTModel->GetMainTracker( m_hObject, nTracker ) == LT_OK)
		{
			//we have the main tracker, see where in its timeline it is
			uint32 nCurrTime;
			uint32 nAnimTime;

			pLTModel->GetCurAnimTime(m_hObject, nTracker, nCurrTime);
			pLTModel->GetCurAnimLength(m_hObject, nTracker, nAnimTime);

			if(nAnimTime)
			{
				//handle wrapping
				nCurrTime %= nAnimTime;

				//ok, now convert cur time to a valid time
				m_tmElapsed = (nCurrTime * GetProps()->m_tmLifespan) / (float)nAnimTime;
			}
			else
			{
				//zero length animation?
				m_tmElapsed = 0.0f;
			}
		}
	}
	else if(GetProps()->m_bSyncToKey)
	{
		//we need to find out where in the key we currently are
		ILTModel		*pLTModel = m_pLTClient->GetModelLT();
		ANIMTRACKERID	nTracker;
		
		if(pLTModel->GetMainTracker( m_hObject, nTracker ) == LT_OK)
		{
			//we have the main tracker, see where in its timeline it is
			uint32 nAnimLength;
			m_pLTClient->GetModelLT()->ResetAnim( m_hObject, nTracker );
			pLTModel->GetCurAnimLength(m_hObject, nTracker, nAnimLength);

			if(nAnimLength > 0)
				nAnimLength--;

			float tmWrappedTime = fmodf(m_tmElapsed / GetProps()->m_tmLifespan, 1.0f);
			uint32 nAnimTime = (uint32)(tmWrappedTime * nAnimLength);
			pLTModel->SetCurAnimTime(m_hObject, nTracker, nAnimTime);
		}
	}
	
	// Base class update first	
	if (!CBaseFX::Update(tmFrameTime)) 
		return false;


	//see if we should reset our model animation
	if(!GetProps()->m_bSyncToKey && IsFinishedShuttingDown())
	{
		//Reset the animation
		ANIMTRACKERID	nTracker;
		
		m_pLTClient->GetModelLT()->GetMainTracker( m_hObject, nTracker );
		m_pLTClient->GetModelLT()->ResetAnim( m_hObject, nTracker );

		if(GetProps()->m_bOverrideAniLength)
			m_pLTClient->GetModelLT()->SetAnimRate( m_hObject, nTracker, m_fAniRate);
	}

	// Align if neccessary, to the rotation of our parent

	if ((m_hParent) && (GetProps()->m_nFacing == FACE_PARENTALIGN))
	{
		LTRotation rParentRot;
		m_pLTClient->GetObjectRotation(m_hParent, &rParentRot);
		rParentRot = (GetProps()->m_bRotate ? rParentRot : rParentRot * m_rNormalRot);
		m_pLTClient->SetObjectRotation(m_hObject, &rParentRot);
	}
	

	// If we want to add a rotation, make sure we are facing the correct way...
	
	if( GetProps()->m_bRotate )
	{
		LTFLOAT		tmFrame = tmFrameTime;
		LTVector	vR( m_rRot.Right() );
		LTVector	vU( m_rRot.Up() );
		LTVector	vF( m_rRot.Forward() );

		LTRotation	rRotation;

		if( m_hCamera && (GetProps()->m_nFacing == FACE_CAMERAFACING))
		{
			m_pLTClient->GetObjectRotation( m_hCamera, &rRotation );
		}
		else
		{
			m_pLTClient->GetObjectRotation( m_hObject, &rRotation );
		}

		m_rRot.Rotate( vR, MATH_DEGREES_TO_RADIANS( GetProps()->m_vRotAdd.x * tmFrame ));
		m_rRot.Rotate( vU, MATH_DEGREES_TO_RADIANS( GetProps()->m_vRotAdd.y * tmFrame ));
		m_rRot.Rotate( vF, MATH_DEGREES_TO_RADIANS( GetProps()->m_vRotAdd.z * tmFrame ));
		
		rRotation = rRotation * m_rRot;
		
		m_pLTClient->SetObjectRotation( m_hObject, &(rRotation * m_rNormalRot));
	}
	else if( GetProps()->m_nFacing == FACE_CAMERAFACING )
	{
		LTRotation rCamRot;

		m_pLTClient->GetObjectRotation( m_hCamera, &rCamRot );
		m_pLTClient->SetObjectRotation( m_hObject, &(rCamRot * m_rNormalRot) );
	}

	// Success !!

	return true;
}
Пример #5
0
bool CLTBModelFX::Init(ILTClient *pClientDE, FX_BASEDATA *pBaseData, const CBaseFXProps *pProps)
{
	// Perform base class initialisation

	if (!CBaseFX::Init(pClientDE, pBaseData, pProps)) 
		return false;

	// Use the "target" Normal instead, if one was specified...

	LTVector vNorm = GetProps()->m_vNorm;

	if( pBaseData->m_vTargetNorm.LengthSquared() > MATH_EPSILON )
	{
		vNorm = pBaseData->m_vTargetNorm;
		vNorm.Normalize();
	}

	// Develop the Right and Up vectors based off the Forward...

	LTVector vR, vU;
	if( (1.0f == vNorm.y) || (-1.0f == vNorm.y) )
	{
		vR = LTVector( 1.0f, 0.0f, 0.0f ).Cross( vNorm );
	}
	else
	{
		vR = LTVector( 0.0f, 1.0f, 0.0f ).Cross( vNorm );
	}

	vU = vNorm.Cross( vR );
	m_rNormalRot = LTRotation( vNorm, vU );


	ObjectCreateStruct ocs;

	// Combine the direction we would like to face with our parents rotation...

	if( m_hParent )
	{
		m_pLTClient->GetObjectRotation( m_hParent, &ocs.m_Rotation );
	}
	else
	{
		ocs.m_Rotation = m_rCreateRot;
	}

	ocs.m_Rotation = ocs.m_Rotation * m_rNormalRot;	

	ocs.m_ObjectType		= OT_MODEL;
	ocs.m_Flags				|= pBaseData->m_dwObjectFlags |	(GetProps()->m_bShadow ? FLAG_SHADOW : 0 );
	ocs.m_Flags2			|= pBaseData->m_dwObjectFlags2;
	
	// Calculate the position with the offset in 'local' coordinate space...

	LTMatrix mMat;
	ocs.m_Rotation.ConvertToMatrix( mMat );

	m_vPos = ocs.m_Pos = m_vCreatePos + (mMat * GetProps()->m_vOffset);

	
	SAFE_STRCPY( ocs.m_Filename, GetProps()->m_szModelName );
	
	SAFE_STRCPY( ocs.m_SkinNames[0], GetProps()->m_szSkinName[0] );
	SAFE_STRCPY( ocs.m_SkinNames[1], GetProps()->m_szSkinName[1] );
	SAFE_STRCPY( ocs.m_SkinNames[2], GetProps()->m_szSkinName[2] );
	SAFE_STRCPY( ocs.m_SkinNames[3], GetProps()->m_szSkinName[3] );
	SAFE_STRCPY( ocs.m_SkinNames[4], GetProps()->m_szSkinName[4] );
	
	SAFE_STRCPY( ocs.m_RenderStyleNames[0], GetProps()->m_szRenderStyle[0] );
	SAFE_STRCPY( ocs.m_RenderStyleNames[1], GetProps()->m_szRenderStyle[1] );
	SAFE_STRCPY( ocs.m_RenderStyleNames[2], GetProps()->m_szRenderStyle[2] );
	SAFE_STRCPY( ocs.m_RenderStyleNames[3], GetProps()->m_szRenderStyle[3] );

	m_hObject = m_pLTClient->CreateObject(&ocs);
	if( !m_hObject ) 
		return LTFALSE;

	ILTModel		*pLTModel = m_pLTClient->GetModelLT();
	ANIMTRACKERID	nTracker;
	
	pLTModel->GetMainTracker( m_hObject, nTracker );

	//setup the animation if the user specified one
	if( strlen(GetProps()->m_szAnimName) > 0)
	{
		//ok, we need to set this
		HMODELANIM hAnim = m_pLTClient->GetAnimIndex(m_hObject, GetProps()->m_szAnimName);

		if(hAnim != INVALID_MODEL_ANIM)
		{
			//ok, lets set this animation
			pLTModel->SetCurAnim(m_hObject, nTracker, hAnim);
			pLTModel->ResetAnim(m_hObject, nTracker);
		}
	}
	//disable looping on this animation (so we can actually stop!)
	pLTModel->SetLooping(m_hObject, nTracker, false);

	// Setup the initial data needed to override the models animation length...
	if( GetProps()->m_bOverrideAniLength )
	{
		uint32 nAnimLength;

		pLTModel->GetCurAnimLength( m_hObject, nTracker, nAnimLength );
		pLTModel->SetCurAnimTime( m_hObject, nTracker, 0 );

		float fAniLength = (GetProps()->m_fAniLength < MATH_EPSILON) ? GetProps()->m_tmLifespan : GetProps()->m_fAniLength;
		
		if( fAniLength >= MATH_EPSILON || fAniLength <= -MATH_EPSILON )
			m_fAniRate = (nAnimLength * 0.001f) / fAniLength;

		pLTModel->SetAnimRate( m_hObject, nTracker, m_fAniRate );
	}

	// Success !!

	return LTTRUE;
}
Пример #6
0
void CNodeController::HandleNodeControlRecoilMessage(HMESSAGEREAD hMessage)
{
	if ( m_cRecoils >= MAX_RECOILS )
		return;

	m_fRecoilTimers[m_cRecoils++] = 0.50f;


	ModelNode eModelNode;
    eModelNode = (ModelNode)g_pLTClient->ReadFromMessageByte(hMessage);

    LTVector vRecoilDir;
    g_pLTClient->ReadFromMessageCompVector(hMessage, &vRecoilDir);

	// Get the magnitude of the recoil vector

    LTFLOAT fRecoilMag = VEC_MAGSQR(vRecoilDir);

	// Get the unit impact/recoil vector

	vRecoilDir /= (float)sqrt(fRecoilMag);

	// Cap it if necessary

	if ( fRecoilMag > 100.0f )
	{
		fRecoilMag = 100.0f;
	}

	// Get the position of the impact

	NSTRUCT* pNode = &m_aNodes[eModelNode];
    ILTModel* pModelLT = g_pLTClient->GetModelLT();
	LTransform transform;
    pModelLT->GetNodeTransform(GetCFX()->GetServerObj(), pNode->hModelNode, transform, LTTRUE);

	// Decompose the transform into the position and rotation

    LTVector vPos;
    ILTTransform* pTransformLT = g_pLTClient->GetTransformLT();
	pTransformLT->GetPos(transform, vPos);

    LTVector vRecoilPos = vPos;

	// Add angular rotations up the recoil parent chain

	ModelNode eModelNodeCurrent = g_pModelButeMgr->GetSkeletonNodeRecoilParent(GetCFX()->GetModelSkeleton(), eModelNode);

	while ( eModelNodeCurrent != eModelNodeInvalid )
	{
		// Get the rotation of the node

		NSTRUCT* pNode = &m_aNodes[eModelNodeCurrent];

		LTransform transform;
        ILTModel* pModelLT = g_pLTClient->GetModelLT();

		// Get the transform of the node we're controlling

        pModelLT->GetNodeTransform(GetCFX()->GetServerObj(), pNode->hModelNode, transform, LTTRUE);

        ILTTransform* pTransformLT = g_pLTClient->GetTransformLT();

		// Decompose the transform into the position and rotation

        LTVector vPos;
        LTRotation rRot;
		pTransformLT->Get(transform, vPos, rRot);

		// Get the rotation vectors of the transform

        LTVector vRight, vUp, vForward;
        g_pLTClient->GetRotationVectors(&rRot, &vUp, &vRight, &vForward);

		// Cross the right vector with the impact vector to get swing

        LTVector vRotationAxis = vRight.Cross(vRecoilDir);
		vRotationAxis.Norm();

		// Add the timed rotation control for the swing

		// !!! HACK
		// !!! Do not add swing if this is a leg node

		if ( !strstr(g_pModelButeMgr->GetSkeletonNodeName(GetCFX()->GetModelSkeleton(), eModelNodeCurrent), "leg") )
		AddNodeControlRotationTimed(eModelNodeCurrent, vRotationAxis, MATH_PI/1000.0f*fRecoilMag, 0.50f);

		// Use the right vector to get twist, but make sure the sign is correct based on location
		// of impact and whether we're getting shot at from behind/front etc

		vRotationAxis = vRight;
		vRotationAxis.Norm();

		// Get the twist

        LTVector vSideDir = vRecoilPos-vPos;
		vSideDir.Norm();

        LTFLOAT fSign = vUp.Dot(vRecoilDir);
		fSign *= vForward.Dot(vSideDir);

		if ( fSign > 0.0f )
		{
			vRotationAxis = -vRotationAxis;
		}

		// Add the timed rotation control for the twist

	//	AddNodeControlRotationTimed(eModelNodeCurrent, vRotationAxis, MATH_PI/1000.0f*fRecoilMag, 0.50f);

		// Decrease the magnitude

		fRecoilMag /= 2.0f;

		eModelNodeCurrent = g_pModelButeMgr->GetSkeletonNodeRecoilParent(GetCFX()->GetModelSkeleton(), eModelNodeCurrent);
	}
}
Пример #7
0
void CNodeController::UpdateHeadFollowPosControl(NCSTRUCT *pNodeControl)
{
    LTVector vPos;
    LTRotation rRot;
	LTransform transform;
    LTVector vU, vR, vF;

	//----------------------------------------------------------------------
	// Get information about the control node...
	// *** NOTE: On the head node... vU faces forward, vR faces down, vF faces right ***

	// Get access to the controls...
    ILTMath *pMathLT = g_pLTClient->GetMathLT();
    ILTModel *pModelLT = g_pLTClient->GetModelLT();
    ILTTransform *pTransformLT = g_pLTClient->GetTransformLT();

	// Get the transform of the node we're controlling
    pModelLT->GetNodeTransform(GetCFX()->GetServerObj(), m_aNodes[pNodeControl->eModelNode].hModelNode, transform, LTTRUE);

	// Decompose the transform into the position and rotation
	pTransformLT->Get(transform, vPos, rRot);
	pMathLT->GetRotationVectors(rRot, vR, vU, vF);

	// Get information about the follow position...
    LTVector vObjPos = pNodeControl->vFollowPos;

	// Turn the follow control off if the expire time has past
	if(pNodeControl->fFollowExpireTime <= 0.0f)
	{
		pNodeControl->fFollowExpireTime = 0.0f;
        pNodeControl->bFollowOn = LTFALSE;
	}
	else
        pNodeControl->fFollowExpireTime -= g_pGameClientShell->GetFrameTime();

	//----------------------------------------------------------------------
	// Setup the rotation matrix to directly follow the destination position

	// Get the direction that we're going to face...
    LTVector vDir = vObjPos - vPos;

	// Setup some temp vectors that are on the x/z plane...
    LTVector vTempU, vTempF, vTempDir;
	vTempU = vU; vTempU.y = 0.0f;
	vTempF = vF; vTempF.y = 0.0f;
	vTempDir = vDir; vTempDir.y = 0.0f;

	VEC_NORM(vTempU);
	VEC_NORM(vTempF);
	VEC_NORM(vTempDir);

	// Get the dot products between the dir vector and the up and forward to determine the rotation angles
    LTFLOAT fDotUDir = VEC_DOT(vTempU, vTempDir);
    LTFLOAT fDotFDir = VEC_DOT(vTempF, vTempDir);
    LTFLOAT fDotRDir = 0.0f;

	// Init the vectors to get a rotation matrix from...
    LTVector vRotAxisR(1.0f, 0.0f, 0.0f);

	// Get the first rotation angle
    LTFLOAT fAngle1 = pNodeControl->bFollowOn ? fDotUDir : 1.0f;
	if(fAngle1 < -0.1f) fAngle1 = -0.1f;		// HACK! Limit the head rotation
	fAngle1 = (1.0f - fAngle1) * MATH_HALFPI;
	if(fDotFDir < 0.0f) fAngle1 *= -1.0f;

	// Do a full rotation around the first axis so we can get an angle for the second axis
    LTFLOAT fTempAngle = pNodeControl->bFollowOn ? ((1.0f - fDotUDir) * MATH_HALFPI) : 0.0f;
	pMathLT->RotateAroundAxis(rRot, vR, (fDotFDir < 0.0f) ? -fTempAngle : fTempAngle);
	pMathLT->GetRotationVectors(rRot, vR, vU, vF);

	VEC_NORM(vDir);
	fDotUDir = VEC_DOT(vU, vDir);
	fDotRDir = VEC_DOT(vR, vDir);

	// Get the second rotation angle
    LTFLOAT fAngle2 = pNodeControl->bFollowOn ? fDotUDir : 1.0f;
	if(fAngle2 < 0.25f) fAngle2 = 0.25f;		// HACK! Limit the head rotation
	fAngle2 = (1.0f - fAngle2) * MATH_HALFPI;
	if(fDotRDir > 0.0f) fAngle2 *= -1.0f;

	// Calculate a max rotation value
    LTFLOAT fRotMax = (pNodeControl->fFollowRate * g_pGameClientShell->GetFrameTime() / 180.0f) * MATH_PI;

	// Interpolate the angles based off the previous angle
	if(fAngle1 > pNodeControl->vFollowAngles.y + fRotMax) fAngle1 = pNodeControl->vFollowAngles.y + fRotMax;
	else if(fAngle1 < pNodeControl->vFollowAngles.y - fRotMax) fAngle1 = pNodeControl->vFollowAngles.y - fRotMax;

	if(fAngle2 > pNodeControl->vFollowAngles.x + fRotMax) fAngle2 = pNodeControl->vFollowAngles.x + fRotMax;
	else if(fAngle2 < pNodeControl->vFollowAngles.x - fRotMax) fAngle2 = pNodeControl->vFollowAngles.x - fRotMax;

	// Create a new rotation and rotate around each controlled axis
    LTRotation rNewRot;
    rNewRot.Init();

	pMathLT->RotateAroundAxis(rNewRot, vRotAxisR, fAngle1);
	pNodeControl->vFollowAngles.y = fAngle1;

	pMathLT->GetRotationVectors(rNewRot, vR, vU, vF);

	pMathLT->RotateAroundAxis(rNewRot, vF, fAngle2);
	pNodeControl->vFollowAngles.x = fAngle2;

	// If we're turned off and back at the start rotation... make the control invalid
	if(!pNodeControl->bFollowOn && pNodeControl->vFollowAngles.x == 0.0f && pNodeControl->vFollowAngles.y == 0.0f)
	{
        pNodeControl->bValid = LTFALSE;
		return;
	}

	// Create a rotation matrix and apply it to the current offset matrix
    LTMatrix m1;
	pMathLT->SetupRotationMatrix(m1, rNewRot);
	m_aNodes[pNodeControl->eModelNode].matTransform = m_aNodes[pNodeControl->eModelNode].matTransform * m1;
}