csgo-2018-source/movieobjects/dmerigconstraintoperators.cpp
2021-07-24 21:11:47 -07:00

2311 lines
78 KiB
C++

//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======
//
// Purpose: Implementation of the constraint operator classes. The constraint
// operators control the position and / or orientation of a specified slave dag
// node based on the position and orientation of a set of weighted target dag
// nodes. The relationship between the target dag node and the slave is
// determined by the type of the constraint. The Operate() function of each
// constraint class is responsible for enforcing its specific relationship.
//
//=============================================================================
#include "movieobjects/dmerigconstraintoperators.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmerig.h"
#include "datamodel/dmelementfactoryhelper.h"
#include "tier1/fmtstr.h"
#include "bone_constraints.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
//
// CDmeConstraintTarget
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Expose the CDmeConstraintTarget class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeConstraintTarget, CDmeConstraintTarget );
//-----------------------------------------------------------------------------
// Purpose: Perform construction tasks, initializes attributes
//-----------------------------------------------------------------------------
void CDmeConstraintTarget::OnConstruction()
{
m_Handle.Init( this, "target" );
m_flWeight.Init( this, "targetWeight" );
m_vecOffset.InitAndSet( this, "vecOffset", vec3_origin );
m_qOffset.InitAndSet( this, "oOffset", quat_identity );
}
//-----------------------------------------------------------------------------
// Purpose: Perform destruction tasks
//-----------------------------------------------------------------------------
void CDmeConstraintTarget::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Purpose: Reset the position and orientation offset values
//-----------------------------------------------------------------------------
void CDmeConstraintTarget::ClearOffset()
{
m_vecOffset = vec3_origin;
m_qOffset = quat_identity;
}
//-----------------------------------------------------------------------------
// Purpose: Find the channel driving the weight value of the constraint
//-----------------------------------------------------------------------------
CDmeChannel *CDmeConstraintTarget::FindWeightChannel() const
{
for ( DmAttributeReferenceIterator_t it = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement( it ) )
{
CDmAttribute *pAttr = g_pDataModel->GetAttribute( it );
CDmElement *pElement = pAttr->GetOwner();
if ( pElement == NULL )
continue;
CDmeChannel *pChannel = CastElement< CDmeChannel >( pElement );
if ( pChannel )
{
if ( pChannel->GetToAttribute() == m_flWeight.GetAttribute() )
return pChannel;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Get the constraint that target belongs to.
//-----------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmeConstraintTarget::GetConstraint()
{
return FindAncestorReferencingElement< CDmeRigBaseConstraintOperator >( this );
}
//-----------------------------------------------------------------------------
// Get the control attached to constraint
//-----------------------------------------------------------------------------
CDmElement *CDmeConstraintTarget::FindWeightControl() const
{
CDmElement *pWeightChannel = FindWeightChannel();
CDmElement *pControl = FindReferringElement< CDmElement >( pWeightChannel, "channel" );
return pControl;
}
//-------------------------------------------------------------------------------------------------
//
// CDmeConstraintSlave
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Expose the CDmeConstraintSlave class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeConstraintSlave, CDmeConstraintSlave );
//-----------------------------------------------------------------------------
// Purpose: Perform construction tasks, initializes attributes
//-----------------------------------------------------------------------------
void CDmeConstraintSlave::OnConstruction()
{
m_Dag.Init( this, "target" );
m_BasePosition.Init( this, TRANSFORM_POSITION );
m_BaseOrientation.Init( this, TRANSFORM_ORIENTATION );
}
//-----------------------------------------------------------------------------
// Purpose: Perform destruction tasks
//-----------------------------------------------------------------------------
void CDmeConstraintSlave::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Get the constraint to which the slave belongs.
//-----------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmeConstraintSlave::GetConstraint() const
{
CDmeRigBaseConstraintOperator *pConstraint = FindAncestorReferencingElement< CDmeRigBaseConstraintOperator >( this );
return pConstraint;
}
//-----------------------------------------------------------------------------
// Purpose: Compute the base world space matrix
//-----------------------------------------------------------------------------
void CDmeConstraintSlave::GetBaseWorldTransform( matrix3x4_t &worldMatrix ) const
{
Vector lsBasePosition = m_BasePosition;
Quaternion lsBaseOrientation = m_BaseOrientation;
CDmeDag *pDag = m_Dag;
if ( pDag )
{
matrix3x4_t parentToWorld, localMatrix;
pDag->GetParentWorldMatrix( parentToWorld );
QuaternionMatrix( lsBaseOrientation, lsBasePosition, localMatrix );
ConcatTransforms( parentToWorld, localMatrix, worldMatrix );
}
else
{
SetIdentityMatrix( worldMatrix );
}
}
//-----------------------------------------------------------------------------
// Purpose: Compute the base position and orientation in world space
//-----------------------------------------------------------------------------
void CDmeConstraintSlave::ComputeBaseWorldValues( Vector &wsPosition, Quaternion &wsOrientation ) const
{
matrix3x4_t worldMatrix;
GetBaseWorldTransform( worldMatrix );
MatrixAngles( worldMatrix, wsOrientation, wsPosition );
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigBaseConstraintOperator
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigBaseConstraintOperator, CDmeRigBaseConstraintOperator );
//-----------------------------------------------------------------------------
// Purpose: Constructor, initializes attributes, create the embedded target
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::OnConstruction()
{
m_Targets.Init( this, "targets" );
m_Slave.InitAndCreate( this, "slave" );
m_mode.InitAndSet( this, "mode", (int)RM_FORWARD );
}
//-----------------------------------------------------------------------------
// Purpose: Perform destruction tasks, destroy the internal elements of the
// constraint.
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::OnDestruction()
{
g_pDataModel->DestroyElement( m_Slave.GetHandle() );
for ( int i = 0 ;i < m_Targets.Count(); ++i )
{
if ( m_Targets[ i ] )
{
g_pDataModel->DestroyElement( m_Targets[ i ]->GetHandle() );
}
}
m_Targets.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Determine if the the constraint has slave with the specified name
//-----------------------------------------------------------------------------
bool CDmeRigBaseConstraintOperator::IsSlaveObject( char const *pchName ) const
{
if ( !m_Slave->m_Dag )
return false;
return !Q_stricmp( m_Slave->m_Dag->GetName(), pchName );
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes that the constraint reads data from, Inputs are
// CDmeDags (handles usually)
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
for ( int i = 0; i < m_Targets.Count(); ++i )
{
if ( !m_Targets[ i ])
continue;
CDmeDag* pDagNode = m_Targets[ i ]->m_Handle;
if ( pDagNode == NULL )
continue;
AddAttribute( attrs, GetInputAttributeType(), pDagNode->GetTransform() );
AddAncestorAttributes( attrs, pDagNode );
CDmAttribute *pWeightAttr = m_Targets[ i ]->GetWeightAttribute();
if ( pWeightAttr )
{
attrs.AddToTail( pWeightAttr );
}
}
// If the slave dag node is the child of another node the operation of the constraint
// is dependent upon the evaluation of the parent because result of the constraint
// operation must be converted into the space of the parent of the slave.
AddAncestorAttributes( attrs, m_Slave->m_Dag );
CDmAttribute *pSlavePosAttr = m_Slave->m_BasePosition.GetAttribute();
if ( pSlavePosAttr )
{
attrs.AddToTail( pSlavePosAttr );
}
CDmAttribute *pSlaveRotAttr = m_Slave->m_BaseOrientation.GetAttribute();
if ( pSlaveRotAttr )
{
attrs.AddToTail( pSlaveRotAttr );
}
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes to which the attribute writes, Outputs are
// CDmeDags (bones or other CDmeRigHandles usually)
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
if ( !m_Slave->m_Dag )
return;
AddAttribute( attrs, GetOutputAttributeType(), m_Slave->m_Dag->GetTransform() );
}
//-----------------------------------------------------------------------------
// Purpose: Add the specified type of attributes from the provided transform
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::AddAttribute( CUtlVector< CDmAttribute * > &attrs, enum EAddType type, CDmeTransform *pTxForm )
{
if ( !pTxForm )
return;
// Get the transform attribute
if ( type & AA_TYPE_POSITION )
{
CDmAttribute *pAttrib = pTxForm->GetPositionAttribute();
if ( pAttrib )
attrs.AddToTail( pAttrib );
}
if ( type & AA_TYPE_ORIENTATION )
{
CDmAttribute *pAttrib = pTxForm->GetOrientationAttribute();
if ( pAttrib )
attrs.AddToTail( pAttrib );
}
}
//-----------------------------------------------------------------------------
// Purpose: Add the position and orientation attributes the entire ancestry of
// the dag node.
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::AddAncestorAttributes( CUtlVector< CDmAttribute * > &attrs, CDmeDag *pDagNode )
{
if ( pDagNode == NULL )
return;
CDmeDag* pParent = pDagNode->GetParent();
while ( pParent )
{
AddAttribute( attrs, (EAddType)( AA_TYPE_POSITION | AA_TYPE_ORIENTATION ), pParent->GetTransform() );
pParent = pParent->GetParent();
}
}
//-----------------------------------------------------------------------------
// Purpose: Add target handles to the the constraint with the specified
// weights. Returns the index of the first target added.
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::AddHandles( int nCount, CDmeDag *const pTargetDags[], const float *pflWeights, bool bPreserveOffset, CUtlVector< CDmeConstraintTarget* > *pTargetList )
{
// If a target list is provided, allocate the space to hold all
// of the constraint targets for the list of input dag nodes.
if ( pTargetList )
{
pTargetList->EnsureCapacity( pTargetList->Count() + nCount );
}
for ( int i = 0; i < nCount; ++i )
{
Assert( pTargetDags[ i ] );
if ( !pTargetDags[ i ] )
continue;
// Check to see if the constraint already has a target with the specified dag node
CDmeConstraintTarget *pTarget = FindConstraintTargetForDag( pTargetDags[ i ] );
if ( pTarget == NULL )
{
pTarget = CreateElement< CDmeConstraintTarget >( CFmtStr( "%s", pTargetDags[ i ]->GetName() ), GetFileId() );
m_Targets.AddToTail( pTarget );
}
if ( pTarget != NULL )
{
pTarget->m_Handle = pTargetDags[ i ];
pTarget->m_flWeight = ( pflWeights != NULL ) ? pflWeights[ i ] : 1.0f;
Vector vOffset = vec3_origin;
Quaternion qOffset = quat_identity;
ComputeOffset( vOffset, qOffset, pTarget, bPreserveOffset );
pTarget->m_vecOffset = vOffset;
pTarget->m_qOffset = qOffset;
if ( pTargetList )
{
pTargetList->AddToTail( pTarget );
}
}
}
PostHandlesAdded( bPreserveOffset );
}
//-----------------------------------------------------------------------------
// Purpose: Remove all target handles from the constraint
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::ClearHandles()
{
m_Targets.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Set the dag node which the constraint is controlling
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::SetSlave( CDmeDag *pSlave )
{
m_Slave->m_Dag = pSlave;
CDmeTransform *pTransform = pSlave->GetTransform();
if ( pTransform )
{
m_Slave->SetBasePosition( pTransform->GetPosition() );
m_Slave->SetBaseOrientation( pTransform->GetOrientation() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Get a pointer to the dag node the constraint is controlling
//-----------------------------------------------------------------------------
const CDmeDag *CDmeRigBaseConstraintOperator::GetSlave() const
{
return m_Slave->m_Dag;
}
//-----------------------------------------------------------------------------
// Purpose: Set the operating mode of the constraint
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::SetMode( RigOperatorMode_t mode )
{
m_mode = mode;
}
//-----------------------------------------------------------------------------
// Purpose: Compute the position and orientation offset of the specified target
// based on the relative transforms of the target and the slave.
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::ComputeOffset( Vector &vOffset, Quaternion &qOffset, CDmeConstraintTarget *pTarget, bool bPreserveOffset )
{
vOffset = vec3_origin;
qOffset = quat_identity;
Assert( m_Slave->m_Dag );
if ( !bPreserveOffset || !m_Slave->m_Dag || !pTarget->m_Handle )
return;
matrix3x4_t mSlave, mTarget;
m_Slave->m_Dag->GetAbsTransform( mSlave );
pTarget->m_Handle->GetAbsTransform( mTarget );
Vector slavePosition, targetPosition;
Quaternion slaveOrientation, targetOrientation;
MatrixAngles( mSlave, slaveOrientation, slavePosition );
MatrixAngles( mTarget, targetOrientation, targetPosition );
vOffset = slavePosition - targetPosition;
Quaternion invTargetOrientation;
QuaternionInvert( targetOrientation, invTargetOrientation );
QuaternionMult( invTargetOrientation, slaveOrientation, qOffset );
}
//-----------------------------------------------------------------------------
// Compute the aggregate target position from the weighted target list and
// return the total weight
//-----------------------------------------------------------------------------
float CDmeRigBaseConstraintOperator::ComputeTargetPosition( Vector &wsTargetPosition )
{
wsTargetPosition = vec3_origin;
float flWeightSum = 0.0f;
int nTargets = m_Targets.Count();
for ( int i = 0; i < nTargets; ++i )
{
CDmeConstraintTarget *pTarget = m_Targets[ i ];
Assert( pTarget );
CDmeDag *pTargetDag = pTarget->m_Handle;
if ( pTargetDag )
{
Vector targetPos;
pTargetDag->GetAbsPosition( targetPos );
wsTargetPosition += ( pTarget->m_flWeight * ( targetPos + pTarget->m_vecOffset ) );
flWeightSum += pTarget->m_flWeight;
}
}
if ( flWeightSum > 0.0f )
{
wsTargetPosition *= 1.0f / flWeightSum;
}
return MIN( 1.0f, flWeightSum );
}
static const int MAX_RIG_TARGETS = 4;
//-----------------------------------------------------------------------------
// Compute the aggregate target orientation from the weighted target list and
// return the total weight
//-----------------------------------------------------------------------------
float CDmeRigBaseConstraintOperator::ComputeTargetOrientation( Quaternion &wsTargetOrientation )
{
int nTargets = m_Targets.Count();
// If there is only one target, for efficiency don't bother with the weighting
if ( nTargets == 1 )
{
CDmeConstraintTarget *pTarget = m_Targets[ 0 ];
CDmeDag *pTargetDag = pTarget->m_Handle;
if ( !pTargetDag )
return 0;
Quaternion targetOrientation;
Quaternion offsetOrientation;
pTargetDag->GetAbsOrientation( targetOrientation );
QuaternionMult( targetOrientation, pTarget->m_qOffset, wsTargetOrientation );
return MIN( 1.0f, pTarget->m_flWeight.Get() );
}
int nAllocCount = MAX( nTargets, 1 );
Quaternion *pQuats = ( Quaternion* )stackalloc( nAllocCount * sizeof( Quaternion ) );
float *flQuatWeights = ( float* )stackalloc( nAllocCount * sizeof( float ) );
wsTargetOrientation = quat_identity;
float flWeightSum = 0.0f;
int nQuatCount = 0;
for ( int i = 0; i < nTargets; ++i )
{
CDmeConstraintTarget *pTarget = m_Targets[ i ];
Assert( pTarget );
CDmeDag *pTargetDag = pTarget->m_Handle;
if ( !pTargetDag )
continue;
Quaternion targetOrientation;
Quaternion offsetOrientation;
pTargetDag->GetAbsOrientation( targetOrientation );
QuaternionMult( targetOrientation, pTarget->m_qOffset, offsetOrientation );
pQuats[ nQuatCount ] = offsetOrientation;
flQuatWeights[ nQuatCount ] = pTarget->m_flWeight;
flWeightSum += pTarget->m_flWeight;
++nQuatCount;
}
QuaternionAverageExponential( wsTargetOrientation, nQuatCount, pQuats, flQuatWeights );
return MIN( 1.0f, flWeightSum );
}
//-----------------------------------------------------------------------------
// Purpose: Get a list of all of the operators that the operation of the
// constraint depends on.
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::GatherInputOperators( CUtlVector< CDmeOperator * > &operatorList )
{
// Iterate through the targets and add the operator
// dependencies for the associated dag nodes.
int nTargets = m_Targets.Count();
for ( int i = 0; i < nTargets; ++i )
{
CDmeConstraintTarget *pTarget = m_Targets[ i ];
Assert( pTarget );
CDmeDag *pHandle = pTarget->m_Handle;
if ( !pHandle )
continue;
pHandle->FindRelevantOperators( operatorList );
}
BaseClass::GatherInputOperators( operatorList );
}
//-----------------------------------------------------------------------------
// Purpose: Disconnect the channels driving the slave dag nodes from the dag
// transforms and connect them to the constraint
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::DisconnectTransformChannels()
{
DisconnectSlaveChannels( m_Slave.GetElement(), GetOutputAttributeType() );
}
//-----------------------------------------------------------------------------
// Purpose: Reconnect the base channels of each slave directly to the dag
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::ReconnectTransformChannels()
{
ReconnectSlaveChannels( m_Slave.GetElement(), GetOutputAttributeType() );
}
//-----------------------------------------------------------------------------
// Purpose: Disconnect the channels driving the specified slave from the dag
// and connect them to the constraint
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::DisconnectSlaveChannels( CDmeConstraintSlave *pSlave, int attributeTypeFlags )
{
// Verify that the specified slave is valid and has a dag node connected.
if ( pSlave == NULL )
return;
if ( pSlave->m_Dag.GetElement() == NULL )
return;
CDmeTransform *pTransform = pSlave->m_Dag->GetTransform();
if ( pTransform == NULL )
return;
// Make the base position and orientation match the
// current position and orientation of the transform.
pSlave->SetBasePosition( pTransform->GetPosition() );
pSlave->SetBaseOrientation( pTransform->GetOrientation() );
// Find the channel targeting the position attribute of the transform and
// re-connect it to the position attribute of the slave within the constraint.
if ( attributeTypeFlags & AA_TYPE_POSITION )
{
CDmeChannel *pPosChannel = FindChannelTargetingElement( pTransform, TRANSFORM_POSITION );
if ( pPosChannel )
{
pPosChannel->SetOutput( pSlave, TRANSFORM_POSITION );
}
}
// Find the channel targeting the orientation attribute of the transform and
// re-connect it to the orientation attribute of the slave within the constraint.
if ( attributeTypeFlags & AA_TYPE_ORIENTATION )
{
CDmeChannel *pRotChannel = FindChannelTargetingElement( pTransform, TRANSFORM_ORIENTATION );
if ( pRotChannel )
{
pRotChannel->SetOutput( pSlave, TRANSFORM_ORIENTATION );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Reconnect the transform channels associated with the specified
// slave directly to the dag
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::ReconnectSlaveChannels( CDmeConstraintSlave *pSlave, int attributeTypeFlags )
{
// Verify that the specified slave is valid and has a dag node connected.
if ( pSlave == NULL )
return;
if ( pSlave->m_Dag.GetElement() == NULL )
return;
CDmeTransform *pTransform = pSlave->m_Dag->GetTransform();
if ( pTransform == NULL )
return;
// Find the channel targeting the position attribute of the slave and
// re-connect it to the position attribute of the transform.
if ( attributeTypeFlags & AA_TYPE_POSITION )
{
CDmeChannel *pPosChannel = FindChannelTargetingElement( pSlave, TRANSFORM_POSITION );
if ( pPosChannel )
{
pPosChannel->SetOutput( pTransform, TRANSFORM_POSITION );
// If the channel is in pass through mode, update the control value with the
// transform value so that the result of the constraint will be maintained.
if ( pPosChannel->GetMode() == CM_PASS )
{
CDmElement *pControl = pPosChannel->GetFromElement();
CDmeTransformControl *pTranformControl = CastElement< CDmeTransformControl >( pControl );
if ( pTranformControl )
{
pTranformControl->SetPosition( pTransform->GetPosition() );
}
}
}
}
// Find the channel targeting the orientation attribute of the slave and
// re-connect it to the orientation attribute of the transform.
if ( attributeTypeFlags & AA_TYPE_ORIENTATION )
{
CDmeChannel *pRotChannel = FindChannelTargetingElement( pSlave, TRANSFORM_ORIENTATION );
if ( pRotChannel )
{
pRotChannel->SetOutput( pTransform, TRANSFORM_ORIENTATION );
// If the channel is in pass through mode, update the control value with the
// transform value so that the result of the constraint will be maintained.
if ( pRotChannel->GetMode() == CM_PASS )
{
CDmElement *pControl = pRotChannel->GetFromElement();
CDmeTransformControl *pTranformControl = CastElement< CDmeTransformControl >( pControl );
if ( pTranformControl )
{
pTranformControl->SetOrientation( pTransform->GetOrientation() );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Find the constraint target for the specified dag node
//-----------------------------------------------------------------------------
CDmeConstraintTarget *CDmeRigBaseConstraintOperator::FindConstraintTargetForDag( CDmeDag* pDagNode ) const
{
if ( pDagNode == NULL )
return NULL;
int nTargets = m_Targets.Count();
for ( int iTarget = 0; iTarget < nTargets; ++iTarget )
{
CDmeConstraintTarget *pTarget = m_Targets[ iTarget ];
if ( pTarget )
{
if ( pTarget->GetDag() == pDagNode )
{
return pTarget;
}
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Find all of the constraints that target the specified dag node
//-----------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::FindDagConstraints( const CDmeDag *pDagNode, CUtlVector< CDmeRigBaseConstraintOperator* > &constraintList )
{
if ( pDagNode == NULL )
return;
// Find the slave instances targeting the specified node
CUtlVector< CDmeConstraintSlave* > slaveList;
FindAncestorsReferencingElement( pDagNode, slaveList );
// Find the constraints for each slave instance
for ( int i = 0; i < slaveList.Count(); ++i )
{
FindAncestorsReferencingElement( slaveList[ i ], constraintList );
}
}
//-----------------------------------------------------------------------------
// Purpose: Find the constraint on the dag of the specified type
//-----------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmeRigBaseConstraintOperator::FindDagConstraint( CDmeDag *pDag, EConstraintType constraintType )
{
CUtlVector< CDmeRigBaseConstraintOperator* > constraintList;
FindDagConstraints( pDag, constraintList );
int nConstraints = constraintList.Count();
for ( int iConstraint = 0; iConstraint < nConstraints; ++iConstraint )
{
CDmeRigBaseConstraintOperator *pConstraint = constraintList[ iConstraint ];
if ( pConstraint )
{
if ( pConstraint->GetConstraintType() == constraintType )
{
return pConstraint;
}
}
}
return NULL;
}
//-------------------------------------------------------------------------------------------------
// Purpose: Remove all of the constraints from the specified dag node.
//-------------------------------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::RemoveConstraintsFromDag( CDmeDag *pDag )
{
if ( pDag == NULL )
return;
// Find the constraints associated with the specified dag node.
CUtlVector< CDmeConstraintSlave* > constraintSlaves;
FindAncestorsReferencingElement( pDag, constraintSlaves );
int nSlaves = constraintSlaves.Count();
for ( int i = 0; i < nSlaves; ++i )
{
CDmeConstraintSlave *pSlave = constraintSlaves[ i ];
if ( pSlave == NULL )
continue;
CDmeRigBaseConstraintOperator *pConstraint = pSlave->GetConstraint();
if ( pConstraint )
{
DestroyConstraint( pConstraint );
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Destroy the specified constraint and remove it from the animation set.
//-------------------------------------------------------------------------------------------------
void CDmeRigBaseConstraintOperator::DestroyConstraint( CDmeRigBaseConstraintOperator *pConstraint )
{
if ( pConstraint == NULL )
return;
CDmeAnimationSet *pAnimSet = FindAncestorReferencingElement< CDmeAnimationSet >( pConstraint );
// Find the weight channels associated with the constraint targets.
const CDmaElementArray< CDmeConstraintTarget > &targetList = pConstraint->GetTargets();
int nTargets = targetList.Count();
for ( int iTarget = 0; iTarget < nTargets; ++iTarget )
{
CDmeConstraintTarget *pTarget = targetList[ iTarget ];
if ( pTarget == NULL )
continue;
CDmeChannel *pWeightChannel = pTarget->FindWeightChannel();
if ( pWeightChannel == NULL )
continue;
// Destroy the control associated with the channel
CDmElement* pControl = pWeightChannel->GetFromElement();
if ( pControl )
{
if ( pAnimSet )
{
pAnimSet->RemoveControl( pControl );
}
CDmeRig::RemoveElementFromRig( pControl );
DestroyElement( pControl );
}
// Remove the channel from the channels clip it belongs to
CDmeChannelsClip *pChannelsClip = FindAncestorReferencingElement< CDmeChannelsClip >( pWeightChannel );
if ( pChannelsClip )
{
pChannelsClip->RemoveChannel( pWeightChannel );
}
// Remove the channel from the rig.
CDmeRig::RemoveElementFromRig( pWeightChannel );
// Destroy the channel
DestroyElement( pWeightChannel );
}
// Reconnect the original channels of the constrained dag back to the transform.
pConstraint->ReconnectTransformChannels();
// Remove the constraint from the animation set's list of operators or
// from the shot if the constraint did not belong to an animation set.
if ( pAnimSet )
{
pAnimSet->RemoveOperator( pConstraint );
}
CDmeFilmClip *pClip = FindAncestorReferencingElement< CDmeFilmClip >( pConstraint );
if ( pClip )
{
pClip->RemoveOperator( pConstraint );
}
// Remove the constraint from the rig it belongs to, if any
CDmeRig::RemoveElementFromRig( pConstraint );
// Destroy the constraint element
DestroyElement( pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Purpose: Get the string name associated with the specified constraint type.
//-------------------------------------------------------------------------------------------------
char const *CDmeRigBaseConstraintOperator::ConstraintTypeName( EConstraintType eType )
{
switch ( eType )
{
case CT_POINT:
return( "pointConstraint" );
case CT_ORIENT:
return( "orientConstraint" );
case CT_AIM:
return( "aimConstraint" );
case CT_IK:
return( "ikConstraint" );
case CT_PARENT:
return( "parentConstraint" );
case CT_ROTATION:
return( "rotationConstraint" );
default:
break;
}
Assert( 0 );
return "unknown";
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigPointConstraintOperator
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Point Constraint
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigPointConstraintOperator, CDmeRigPointConstraintOperator );
void CDmeRigPointConstraintOperator::OnConstruction()
{
}
void CDmeRigPointConstraintOperator::OnDestruction()
{
}
void CDmeRigPointConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigPointConstraintOperator::Operate", "SFM" );
CDmeDag *pSlaveDag = m_Slave->GetDag();
if ( !pSlaveDag )
return;
// Calculate the current target position and the total weight
// of the the targets contributing to the target position.
Vector targetPos;
float weight = ComputeTargetPosition( targetPos );
// Blend between the target position and the base position using the target weight
Vector finalPos = targetPos;
if ( weight < 1.0f )
{
Vector basePosition;
Quaternion baseOrientation;
m_Slave->ComputeBaseWorldValues( basePosition, baseOrientation );
finalPos = ( targetPos * weight ) + ( basePosition * ( 1.0f - weight ) );
}
// Update the transform of the dag with the new position.
pSlaveDag->SetAbsPosition( finalPos );
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigOrientConstraintOperator
//
//-------------------------------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigOrientConstraintOperator, CDmeRigOrientConstraintOperator );
void CDmeRigOrientConstraintOperator::OnConstruction()
{
}
void CDmeRigOrientConstraintOperator::OnDestruction()
{
}
void CDmeRigOrientConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigOrientConstraintOperator::Operate", "SFM" );
CDmeDag *pSlaveDag = m_Slave->GetDag();
if ( !pSlaveDag )
return;
// Compute the target orientation and weight
Quaternion qTarget;
float weight = ComputeTargetOrientation( qTarget );
// Blend between the target orientation and the base orientation using the target weight
Quaternion qFinalWorld = qTarget;
if ( weight < 1.0f )
{
Vector basePosition;
Quaternion baseOrientation;
m_Slave->ComputeBaseWorldValues( basePosition, baseOrientation );
QuaternionSlerp( baseOrientation, qTarget, weight, qFinalWorld );
}
matrix3x4_t parentToWorld;
pSlaveDag->GetParentWorldMatrix( parentToWorld );
Quaternion qParentToWorld;
MatrixQuaternion( parentToWorld, qParentToWorld );
Quaternion qParentToWorldInv;
QuaternionInvert( qParentToWorld, qParentToWorldInv );
Quaternion qFinalLocal;
QuaternionMult( qParentToWorldInv, qFinalWorld, qFinalLocal );
pSlaveDag->GetTransform()->SetOrientation( qFinalLocal );
/*
CDmeDag *pSlaveDag = m_Slave->GetDag();
if ( !pSlaveDag )
return;
// Compute the target orientation and weight
Quaternion qTarget;
float weight = ComputeTargetOrientation( qTarget );
// Blend between the target orientation and the base orientation using the target weight
Vector basePosition;
Quaternion baseOrientation;
m_Slave->ComputeBaseWorldValues( basePosition, baseOrientation );
Quaternion qFinal;
QuaternionSlerp( baseOrientation, qTarget, weight, qFinal );
// Update the orientation of the slave, but maintain the position.
matrix3x4_t absTxForm;
pSlaveDag->GetAbsTransform( absTxForm );
Vector worldPos;
MatrixGetColumn( absTxForm, 3, worldPos );
AngleMatrix( qFinal, worldPos, absTxForm );
pSlaveDag->SetAbsTransform( absTxForm );
*/
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigAimConstraintOperator
//
//-------------------------------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigAimConstraintOperator, CDmeRigAimConstraintOperator );
void CDmeRigAimConstraintOperator::OnConstruction()
{
m_AimOffset.InitAndSet( this, "aimOffset", quat_identity );
m_UpVector.InitAndSet( this, "upVector", Vector( 0, 0, 1 ) );
m_UpSpaceTarget.Init( this, "upSpaceTarget" );
m_UpType.InitAndSet( this, "upType", CConstraintBones::AC_UP_TYPE_OBJECT_ROTATION );
}
void CDmeRigAimConstraintOperator::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Purpose: Perform an update after targets are added to the constraint. Offset
// means something slightly different for aim constraint, it's the rotation
// needed to orient the aimVector back at the original slave object. So all
// target offsets are simply cleared out.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::PostHandlesAdded( bool bPreserveOffset )
{
for ( int i = 0; i < m_Targets.Count(); ++i )
{
m_Targets[ i ]->ClearOffset();
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the up vector to be used when calculating the orientation from
// the target position. The up vector may be in world space or in the space of
// a specified dag node.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::SetUpVector( const Vector &upVector, bool bPreserveOffset, const CDmeDag *pUpSpaceTarget )
{
CDmeDag *pDag = m_Slave->GetDag();
if ( pDag == NULL )
return;
m_UpVector = upVector.Normalized();
m_UpSpaceTarget = pUpSpaceTarget;
UpdateOffset( bPreserveOffset );
}
//-----------------------------------------------------------------------------
// Purpose: Set the up vector to be used when calculating the orientation from
// the target position. The up vector may be in world space or in the space of
// a specified dag node.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::SetUpType( int nUpType )
{
if ( nUpType < CConstraintBones::AC_UP_TYPE_FIRST || nUpType > CConstraintBones::AC_UP_TYPE_LAST )
{
nUpType = CConstraintBones::AC_UP_TYPE_OBJECT_ROTATION;
}
m_UpType = CConstraintBones::AC_UP_TYPE_OBJECT_ROTATION;
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the orientation needed to make a transform where the y
// vector of the transform matches the forward vector and the z vector matches
// the up reference vector as closely as possible. The x vector will be in the
// plane defined by using the forward vector as the normal.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::AimAt( const Vector &vecForward, const Vector &referenceUp, Quaternion &q )
{
Vector forward = vecForward;
forward.NormalizeInPlace();
float ratio = DotProduct( forward, referenceUp );
Vector up = referenceUp - ( forward * ratio );
up.NormalizeInPlace();
Vector right = forward.Cross( up );
right.NormalizeInPlace();
const Vector &x = right;
const Vector &y = forward;
const Vector &z = up;
float tr = x.x + y.y + z.z;
q.Init( y.z - z.y , z.x - x.z, x.y - y.x, tr + 1.0f );
float radius = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
if ( radius > FLT_EPSILON )
{
QuaternionNormalize( q );
}
else
{
matrix3x4_t rotMat;
MatrixSetColumn( x, 0, rotMat );
MatrixSetColumn( y, 1, rotMat );
MatrixSetColumn( z, 2, rotMat );
MatrixQuaternion( rotMat, q );
}
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the target orientation of the aim constraint based on the
// current slave position and the target position, using the up vector as a
// reference to try and maintain.
//-----------------------------------------------------------------------------
float CDmeRigAimConstraintOperator::CalculateOrientation( Quaternion &targetOrientation )
{
CDmeDag *pDag = m_Slave->GetDag();
if ( pDag == NULL )
return 0.0f;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return 0.0f;
// Compute the world space position of the target
Vector wsTargetPos;
float weight = ComputeTargetPosition( wsTargetPos );
// Construct the matrix to convert from world space to the parent space of the slave
matrix3x4_t parentToWorld;
matrix3x4_t worldToParent;
pDag->GetParentWorldMatrix( parentToWorld );
MatrixInvert( parentToWorld, worldToParent );
// If the up vector is in world space, convert it into local space
Vector worldUp;
Vector localUp;
const CDmeDag *pUpSpaceDag = m_UpSpaceTarget;
switch ( m_UpType )
{
case CConstraintBones::AC_UP_TYPE_VECTOR:
VectorCopy( m_UpVector, worldUp );
break;
case CConstraintBones::AC_UP_TYPE_OBJECT:
if ( pUpSpaceDag )
{
matrix3x4_t localToWorld;
pUpSpaceDag->GetAbsTransform( localToWorld );
Vector vUpObjectWorldPos;
MatrixPosition( localToWorld, vUpObjectWorldPos );
Vector vSlaveWorldPos;
VectorTransform( pTransform->GetPosition(), parentToWorld, vSlaveWorldPos );
VectorSubtract( vUpObjectWorldPos, vSlaveWorldPos, worldUp );
VectorNormalize( worldUp );
}
else
{
VectorCopy( m_UpVector, worldUp );
}
break;
case CConstraintBones::AC_UP_TYPE_PARENT_ROTATION:
VectorRotate( m_UpVector, parentToWorld, worldUp );
break;
default:
case CConstraintBones::AC_UP_TYPE_OBJECT_ROTATION:
if ( pUpSpaceDag )
{
matrix3x4_t localToWorld;
pUpSpaceDag->GetAbsTransform( localToWorld );
VectorRotate( m_UpVector, localToWorld, worldUp );
}
else
{
VectorCopy( m_UpVector, worldUp );
}
break;
}
VectorRotate( worldUp, worldToParent, localUp );
// Convert the target's world space position into the local space of the slave.
Vector lsTargetPos;
VectorTransform( wsTargetPos, worldToParent, lsTargetPos );
// Compute the local space forward vector
Vector slavePos = pTransform->GetPosition();
Vector forward = lsTargetPos - slavePos;
forward.NormalizeInPlace();
// Compute the orientation
AimAt( forward, localUp, targetOrientation );
{
Quaternion qAim;
matrix3x4_t mUpToWorld;
if ( pUpSpaceDag )
{
pUpSpaceDag->GetAbsTransform( mUpToWorld );
}
else
{
SetIdentityMatrix( mUpToWorld );
}
CConstraintBones::ComputeAimConstraint( qAim, wsTargetPos, parentToWorld, m_UpVector.Get(), slavePos, &mUpToWorld, static_cast< CConstraintBones::AimConstraintUpType_t >( m_UpType.Get() ) );
Assert( QuaternionsAreEqual( qAim, targetOrientation, 1.0e-5 ) );
}
return weight;
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the rotational offset which will be applied after aiming
// the transform at the target location. The rotational offset essentially
// allows the selection of which direction within the slave's space will be
// considered the forward direction and will point towards the target.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::UpdateOffset( bool bPreserveOffset )
{
if ( !bPreserveOffset )
{
m_AimOffset = quat_identity;
return;
}
CDmeDag *pDag = m_Slave->GetDag();
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
// Calculate the desired orientation based the target position
Quaternion targetOrientation;
CalculateOrientation( targetOrientation );
// Compute the difference between the slave's current orientation and the target orientation
Quaternion slaveOrientation = pTransform->GetOrientation();
Quaternion qInv, aimOffset;
QuaternionInvert( targetOrientation, qInv );
QuaternionMult( qInv, slaveOrientation, aimOffset );
m_AimOffset = aimOffset;
}
//-----------------------------------------------------------------------------
// Purpose: Operate the aim constraint, modifying the orientation of the slave
// so that it looks at the target position.
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigAimConstraintOperator::Operate", "SFM" );
CDmeDag *pDag = m_Slave->GetDag();
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
// Calculate the desired orientation based the target position
Quaternion targetOrientation;
float targetWeight = CalculateOrientation( targetOrientation );
// Add in initial offset
Quaternion offsetOrientation;
QuaternionMult( targetOrientation, m_AimOffset, offsetOrientation );
// Blend between the target orientation and the base orientation using the target weight
Quaternion finalOrientation;
Quaternion baseOrientation = m_Slave->GetBaseOrientation();
QuaternionSlerp( baseOrientation, offsetOrientation, targetWeight, finalOrientation );
// Update the orientation of the slave
pTransform->SetOrientation( finalOrientation );
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes that the constraint reads data from, Inputs are
// CDmeDags (handles usually)
//-----------------------------------------------------------------------------
void CDmeRigAimConstraintOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
CDmeRigBaseConstraintOperator::GetInputAttributes( attrs );
// The position of the slave must be evaluated be
CDmeDag *pSlaveDag = m_Slave->GetDag();
if ( pSlaveDag )
{
AddAttribute( attrs, AA_TYPE_POSITION, pSlaveDag->GetTransform() );
}
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigRotationConstraintOperator
//
//-------------------------------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigRotationConstraintOperator, CDmeRigRotationConstraintOperator );
//-----------------------------------------------------------------------------
// Purpose: Perform post construction operations, including initializing member
// attributes.
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::OnConstruction()
{
m_Rotations.Init( this, "rotations" );
m_Axies.Init( this, "axies" );
}
//-----------------------------------------------------------------------------
// Purpose: Perform shutdown and cleanup operations.
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Purpose: Run the operator, this calculates the resulting quaternion
// orientation and stores it in result.
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigRotationConstraintOperator::Operate", "SFM" );
CDmeDag *pDag = m_Slave->GetDag();
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
Quaternion finalOrientation = quat_identity;
int nAxies = MIN( m_Axies.Count(), m_Rotations.Count() );
for ( int iAxis = 0; iAxis < nAxies; ++iAxis )
{
Vector axis = m_Axies[ iAxis ];
float rotation = m_Rotations[ iAxis ];
Quaternion orientation;
AxisAngleQuaternion( axis, rotation, orientation );
QuaternionNormalize( orientation );
QuaternionMult( orientation, finalOrientation, finalOrientation );
QuaternionNormalize( finalOrientation );
}
// Update the orientation of the slave
pTransform->SetOrientation( finalOrientation );
}
//-----------------------------------------------------------------------------
// Purpose: Add the input attribute used by the operator to the provided list
// of attributes, This is generally used by the evaluation process to find the
// attributes an operator is dependent on.
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
attrs.AddToTail( m_Axies.GetAttribute() );
attrs.AddToTail( m_Rotations.GetAttribute() );
}
//-----------------------------------------------------------------------------
// Purpose: Add a rotation axis
//-----------------------------------------------------------------------------
int CDmeRigRotationConstraintOperator::AddAxis( const Vector &axis )
{
int index = m_Axies.AddToTail( axis );
m_Rotations.AddToTail( 0.0f );
Assert( m_Rotations.Count() == m_Axies.Count() );
return index;
}
//-----------------------------------------------------------------------------
// Purpose: Set the axis around which the rotation is to occur
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::SetAxis( const Vector &axis, int index )
{
if ( index < m_Axies.Count() )
{
m_Axies.Set( index, axis );
}
}
//-----------------------------------------------------------------------------
// Purpose: Set current rotation value
//-----------------------------------------------------------------------------
void CDmeRigRotationConstraintOperator::SetRotation( float rotation, int index )
{
if ( index < m_Rotations.Count() )
{
m_Rotations.Set( index, rotation );
}
}
//-------------------------------------------------------------------------------------------------
//
// Parent Constraint
//
//-------------------------------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigParentConstraintOperator, CDmeRigParentConstraintOperator );
void CDmeRigParentConstraintOperator::OnConstruction()
{
}
void CDmeRigParentConstraintOperator::OnDestruction()
{
}
//-------------------------------------------------------------------------------------------------
// Compute the offsets of the specified target based on the relative transforms of the target to
// the slave
//-------------------------------------------------------------------------------------------------
void CDmeRigParentConstraintOperator::ComputeOffset( Vector &vOffset, Quaternion &qOffset, CDmeConstraintTarget *pTarget, bool bPreserveOffset )
{
CDmeDag *pSlaveDag = m_Slave->GetDag();
CDmeDag *pTargetDag = pTarget->GetDag();
Assert( pSlaveDag );
if ( !bPreserveOffset || !pSlaveDag|| !pTargetDag )
{
vOffset = vec3_origin;
qOffset = quat_identity;
return;
}
// Get slave abs info
matrix3x4_t mS;
m_Slave->GetBaseWorldTransform( mS );
matrix3x4_t mT;
pTargetDag->GetAbsTransform( mT );
matrix3x4_t invT;
MatrixInvert( mT, invT );
// Compute slave offset local to target transform
matrix3x4_t offset;
ConcatTransforms( invT, mS, offset );
MatrixAngles( offset, qOffset, vOffset );
}
//-------------------------------------------------------------------------------------------------
// Compute the aggregate target position and orientation from the weighted target list and return
// the resulting position and orientation in addition to updating the target dag.
//-------------------------------------------------------------------------------------------------
float CDmeRigParentConstraintOperator::ComputeTargetPositionOrientation( Vector &wsTargetPos, Quaternion &wsTargetOrientation )
{
static Quaternion s_Quats[ MAX_RIG_TARGETS ];
static float s_flQuatWeights[ MAX_RIG_TARGETS ];
Quaternion *pQuats = s_Quats;
float *flQuatWeights = s_flQuatWeights;
int nTargets = m_Targets.Count();
if ( nTargets > MAX_RIG_TARGETS )
{
pQuats = (Quaternion *)stackalloc( nTargets * sizeof( Quaternion ) );
flQuatWeights = (float *)stackalloc( nTargets * sizeof( float ) );
}
float weightSum = 0.0f;
wsTargetPos = vec3_origin;
wsTargetOrientation = quat_identity;
int nQuatCount = 0;
for ( int i = 0; i < nTargets; ++i )
{
CDmeConstraintTarget *pTarget = m_Targets[ i ];
Assert( pTarget );
CDmeDag *pTargetDag = pTarget->GetDag();
if ( !pTargetDag )
continue;
float flWeight = pTarget->GetWeight();
matrix3x4_t handleM;
pTargetDag->GetAbsTransform( handleM );
matrix3x4_t offset;
Vector vOffset = pTarget->GetPositionOfffset();
Quaternion qOffset = pTarget->GetOrientationOffset();
AngleMatrix( RadianEuler( qOffset ), vOffset, offset );
matrix3x4_t absTxForm;
ConcatTransforms( handleM, offset, absTxForm );
Vector pos;
Quaternion q;
MatrixAngles( absTxForm, q, pos );
wsTargetPos += ( flWeight * pos );
pQuats[ nQuatCount ] = q;
flQuatWeights[ nQuatCount ] = flWeight;
++nQuatCount;
// For normalization
weightSum += flWeight;
}
if ( weightSum > 0.0f )
{
wsTargetPos *= 1.0f / weightSum;
}
QuaternionAverageExponential( wsTargetOrientation, nQuatCount, pQuats, flQuatWeights );
return MIN( 1.0f, weightSum );
}
void CDmeRigParentConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigParentConstraintOperator::Operate", "SFM" );
CDmeDag *pSlaveDag = m_Slave->GetDag();
if ( !pSlaveDag )
return;
// Compute the target orientation and weight
Vector targetPosition;
Quaternion targetOrientation;
float weight = ComputeTargetPositionOrientation( targetPosition, targetOrientation );
Vector finalPosition = targetPosition;
Quaternion finalOrientation = targetOrientation;
// Blend between the target orientation and the base orientation using the target weight
if ( weight < 1.0f )
{
Vector basePosition;
Quaternion baseOrientation;
m_Slave->ComputeBaseWorldValues( basePosition, baseOrientation );
VectorLerp( basePosition, targetPosition, weight, finalPosition );
QuaternionSlerp( baseOrientation, targetOrientation, weight, finalOrientation );
}
matrix3x4_t finalTransform;
AngleMatrix( RadianEuler( finalOrientation ), finalPosition, finalTransform );
pSlaveDag->SetAbsTransform( finalTransform );
}
//-----------------------------------------------------------------------------
// 3 joint (2 bone) IK Constraint
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigIKConstraintOperator, CDmeRigIKConstraintOperator );
//-----------------------------------------------------------------------------
// Purpose: Perform initialization operations.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::OnConstruction()
{
m_StartOffsetRotation.InitAndSet( this, "startOffset", quat_identity );
m_MidOffsetRotation.InitAndSet( this, "midOffset", quat_identity );
m_PoleVector.InitAndSet( this, "poleVector", Vector( 0, 0, 1 ) );
m_PoleVectorTarget.Init( this, "pvTarget" );
m_StartJoint.InitAndCreate( this, "startJoint" );
m_MidJoint.InitAndCreate( this, "midJoint" );
m_EndJoint.InitAndCreate( this, "endJoint" );
}
//-----------------------------------------------------------------------------
// Purpose: Perform shutdown operations
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::OnDestruction()
{
g_pDataModel->DestroyElement( m_StartJoint );
g_pDataModel->DestroyElement( m_MidJoint );
g_pDataModel->DestroyElement( m_EndJoint );
}
//-----------------------------------------------------------------------------
// Purpose: Validate that the start and end effector are set up and that there
// is only one intermediate bone between them forming a 2 bone ik chain. If
// specified calculate the offset of the current position of the bones from the
// target position calculated by the ik solution and store as an offset to be
// applied upon operation.
//-----------------------------------------------------------------------------
bool CDmeRigIKConstraintOperator::Setup( bool bPreserveOffset )
{
CDmeDag *pStartJoint = m_StartJoint->GetDag();
CDmeDag *pMidJoint = m_MidJoint->GetDag();
CDmeDag *pEndJoint = m_EndJoint->GetDag();
CDmeTransform *pStartTransform = pStartJoint->GetTransform();
CDmeTransform *pMidTransform = pMidJoint->GetTransform();
// Make sure all the joints have been specified.
if ( !pStartJoint || !pMidJoint || !pEndJoint )
return false;
// Verify the chain
if ( ( pEndJoint->GetParent() != pMidJoint ) || ( pMidJoint->GetParent() != pStartJoint ) )
return false;
// Make sure there is one and only one target and that it is valid
if ( m_Targets.Count() != 1 )
return false;
CDmeDag *pTargetDag = m_Targets[ 0 ]->GetDag();
if ( pTargetDag == NULL )
return false;
// Calculate the orientation that the start and end joints would be set to if the ik were run,
// the calculate the difference between the result and the current orientation.
if ( bPreserveOffset )
{
Quaternion qStartIK = quat_identity;
Quaternion qMidIK = quat_identity;
CalculateOrientations( qStartIK, qMidIK, quat_identity, quat_identity );
// Calculate the offset of the start joint
Quaternion qStartCurrent = pStartTransform->GetOrientation();
Quaternion qStartInv, qStartOffset;
QuaternionInvert( qStartIK, qStartInv );
QuaternionMult( qStartInv, qStartCurrent, qStartOffset );
m_StartOffsetRotation = qStartOffset;
// Calculate the offset of the mid joint, note the ik solve has to be re-run
// with the offset of the start joint to get the proper offset for the mid joint.
CalculateOrientations( qStartIK, qMidIK, m_StartOffsetRotation, quat_identity );
Quaternion qMidCurrent = pMidTransform->GetOrientation();
Quaternion qMidInv, qMidOffset;
QuaternionInvert( qMidIK, qMidInv );
QuaternionMult( qMidInv, qMidCurrent, qMidOffset );
m_MidOffsetRotation = qMidOffset;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the orientation needed to make a transform where the x
// vector of the transform matches the forward vector and the z vector matches
// the up reference vector as closely as possible. The y vector will be in the
// plane defined by using the forward vector as the normal.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::AimAt( const Vector &vecForward, const Vector &referenceUp, Quaternion &q )
{
Vector forward = vecForward;
forward.NormalizeInPlace();
float ratio = DotProduct( forward, referenceUp );
Vector up = referenceUp - ( forward * ratio );
up.NormalizeInPlace();
Vector left = up.Cross( forward );
left.NormalizeInPlace();
const Vector &x = forward;
const Vector &y = left;
const Vector &z = up;
float tr = x.x + y.y + z.z;
q.Init( y.z - z.y, z.x - x.z, x.y - y.x, tr + 1.0f );
float radius = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
if ( radius > FLT_EPSILON )
{
QuaternionNormalize( q );
}
else
{
matrix3x4_t rotMat;
MatrixSetColumn( x, 0, rotMat );
MatrixSetColumn( y, 1, rotMat );
MatrixSetColumn( z, 2, rotMat );
MatrixQuaternion( rotMat, q );
}
}
//-----------------------------------------------------------------------------
// Purpose: Perform the 2 bone ik solve to get target orientation for the
// start and mid bones and apply the specified offset rotations to get the
// final target orientation for each bone.
//-----------------------------------------------------------------------------
float CDmeRigIKConstraintOperator::CalculateOrientations( Quaternion &startBoneOrientation, Quaternion &midBoneOrientation, const Quaternion &startOffset, const Quaternion &midOffset )
{
// Get the pointers to the dag nodes and transforms that participate in the calculations.
CDmeDag *pStartJoint = m_StartJoint->GetDag();
CDmeDag *pMidJoint = m_MidJoint->GetDag();
CDmeDag *pEndJoint = m_EndJoint->GetDag();
CDmeTransform *pStartTransform = pStartJoint->GetTransform();
CDmeTransform *pMidTransform = pMidJoint->GetTransform();
CDmeTransform *pEndTransform = pEndJoint->GetTransform();
// Get the transform matrix of the parent of the start joint, this transform defines the
// local space of the start joint, which is the space in which the calculations will be done.
matrix3x4_t mLocalToWorld;
matrix3x4_t mWorldToLocal;
pStartJoint->GetParentWorldMatrix( mLocalToWorld );
MatrixInvert( mLocalToWorld, mWorldToLocal );
// Update the target position and get the result in world space
Vector wsTargetPos;
float weight = ComputeTargetPosition( wsTargetPos );
// Convert the world space target position into the local space
Vector lsTargetPos;
VectorTransform( wsTargetPos, mWorldToLocal, lsTargetPos );
// Find the axis, which is the vector from the start joint to the end joint and then calculate
// the distance of the axis and each of the bones. Additionally calculate vDir, the unit vector
// along the direction of the axis.
Vector startPos = pStartTransform->GetPosition();
Vector vAxis = lsTargetPos - startPos;
Vector vDir = vAxis;
float axisLen = VectorNormalize( vDir );
float boneLenA = pMidTransform->GetPosition().Length();
float boneLenB = pEndTransform->GetPosition().Length();
axisLen = MIN( axisLen, boneLenA + boneLenB );
lsTargetPos = startPos + ( vDir * axisLen );
// Find the up vector, this is the vector in plane perpendicular to the axis along which the
// mid point will move as a function of the length of the axis.
Vector vPole;
CDmeDag *pPoleVectorTarget = m_PoleVectorTarget;
if ( pPoleVectorTarget )
{
Vector wsPVTargetPos;
pPoleVectorTarget->GetAbsPosition( wsPVTargetPos );
Vector lsPVTargetPos;
VectorTransform( wsPVTargetPos, mWorldToLocal, lsPVTargetPos );
vPole = lsPVTargetPos - startPos;
}
else
{
VectorRotate( m_PoleVector, mWorldToLocal, vPole );
}
Vector vUp = vPole - ( vDir * DotProduct( vDir, vPole ) );
VectorNormalize( vUp );
// Calculate the distance from the start point to the axis mid point. The axis mid point is the
// location on the axis that is the projection of the final mid point position onto the axis.
float midAxisLen = ( ( boneLenA * boneLenA ) - ( boneLenB * boneLenB ) + ( axisLen * axisLen ) ) / ( 2.0f * axisLen );
// Calculate the distance of the mid point from the axis
float midDist = sqrt( MAX( 0, ( boneLenA * boneLenA ) - ( midAxisLen * midAxisLen ) ) );
// Calculate the vector to the new mid point position, and then calculate the new mid point position.
Vector vMid = ( vDir * midAxisLen ) + ( vUp * midDist );
Vector newMidPos = startPos + vMid;
// Calculate the orientation of the first bone
Quaternion qStartTarget;
AimAt( vMid, vUp, qStartTarget );
QuaternionMult( qStartTarget, startOffset, startBoneOrientation );
// Calculate the orientation of the second bone
Vector vEnd = lsTargetPos - newMidPos;
Quaternion qParentBoneOrientB;
Quaternion qInvBoneOrientA;
Quaternion qMidTarget;
AimAt( vEnd, vUp, qParentBoneOrientB );
QuaternionInvert( startBoneOrientation, qInvBoneOrientA );
QuaternionMult( qInvBoneOrientA, qParentBoneOrientB, qMidTarget );
QuaternionMult( qMidTarget, midOffset, midBoneOrientation );
return weight;
}
//-----------------------------------------------------------------------------
// Purpose: Run the ik solve and apply the results to the constrained bones.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigIKConstraintOperator::Operate", "SFM" );
CDmeDag *pStartJoint = m_StartJoint->GetDag();
CDmeDag *pMidJoint = m_MidJoint->GetDag();
CDmeDag *pEndJoint = m_EndJoint->GetDag();
// Make sure all the joints have been specified.
if ( !pStartJoint || !pMidJoint || !pEndJoint )
return;
// Calculate the new orientation of the start and mid joints
Quaternion qStartTarget = quat_identity;
Quaternion qMidTarget = quat_identity;
float targetWeight = CalculateOrientations( qStartTarget, qMidTarget, m_StartOffsetRotation, m_MidOffsetRotation );
// Update the orientations of the start and mid point joints
CDmeTransform *pStartTransform = m_StartJoint->GetDag()->GetTransform();
CDmeTransform *pMidTransform = m_MidJoint->GetDag()->GetTransform();
Quaternion qStartBase = m_StartJoint->GetBaseOrientation();
Quaternion qMidBase = m_MidJoint->GetBaseOrientation();
Quaternion qStartFinal, qMidFinal;
QuaternionSlerp( qStartBase, qStartTarget, targetWeight, qStartFinal );
QuaternionSlerp( qMidBase, qMidTarget, targetWeight, qMidFinal );
pStartTransform->SetOrientation( qStartFinal );
pMidTransform->SetOrientation( qMidFinal );
}
//-----------------------------------------------------------------------------
// Purpose: Assign the dag nodes that to be controlled by the ik constraint.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::SetJoints( CDmeDag *pStartJoint, CDmeDag *pMidJoint, CDmeDag *pEndJoint )
{
m_StartJoint->SetDag( pStartJoint );
m_MidJoint->SetDag( pMidJoint );
m_EndJoint->SetDag( pEndJoint );
}
//-----------------------------------------------------------------------------
// Purpose: Set the world space pole vector for the ik constraint
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::SetPoleVector( const Vector &poleVector )
{
m_PoleVector = poleVector;
}
//-----------------------------------------------------------------------------
// Purpose: Set the pole vector target, a dag node toward which the pole vector
// will point, overrides the standard pole vector.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::SetPoleVectorTarget( CDmeDag *pPoleVectorTarget )
{
m_PoleVectorTarget = pPoleVectorTarget;
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes which are written to by the constraint.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
AddAttribute( attrs, AA_TYPE_ORIENTATION, m_StartJoint->GetTransform() );
AddAttribute( attrs, AA_TYPE_ORIENTATION, m_MidJoint->GetTransform() );
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes upon which the constraint is dependent.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
BaseClass::GetInputAttributes( attrs );
AddAncestorAttributes( attrs, m_StartJoint->GetDag() );
AddAttribute( attrs, AA_TYPE_POSITION, m_StartJoint->GetTransform() );
AddAttribute( attrs, AA_TYPE_POSITION, m_MidJoint->GetTransform() );
AddAttribute( attrs, AA_TYPE_POSITION, m_EndJoint->GetTransform() );
}
//-----------------------------------------------------------------------------
// Purpose: Find all of the channels relevant to all of the target handles
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::GatherInputOperators( CUtlVector< CDmeOperator * > &operatorList )
{
if ( m_PoleVectorTarget.GetElement() )
{
m_PoleVectorTarget->FindRelevantOperators( operatorList );
}
BaseClass::GatherInputOperators( operatorList );
}
//-----------------------------------------------------------------------------
// Purpose: Compute the offset for the specified target. The ik constraint does
// not use target offsets, so this function simply clears the offset for the
// specified target.
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::ComputeOffset( Vector &vOffset, Quaternion &qOffset, CDmeConstraintTarget *pTarget, bool bPreserveOffset )
{
// No target offset allowed
vOffset = vec3_origin;
qOffset = quat_identity;
}
//-----------------------------------------------------------------------------
// Purpose: Disconnect the channels driving the slave dag nodes from the dag
// transforms and connect them to the constraint
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::DisconnectTransformChannels()
{
DisconnectSlaveChannels( m_StartJoint.GetElement(), AA_TYPE_ORIENTATION );
DisconnectSlaveChannels( m_MidJoint.GetElement(), AA_TYPE_ORIENTATION );
}
//-----------------------------------------------------------------------------
// Purpose: Reconnect the base channels of each slave directly to the dag
//-----------------------------------------------------------------------------
void CDmeRigIKConstraintOperator::ReconnectTransformChannels()
{
ReconnectSlaveChannels( m_StartJoint.GetElement(), AA_TYPE_ORIENTATION );
ReconnectSlaveChannels( m_MidJoint.GetElement(), AA_TYPE_ORIENTATION );
}
//-----------------------------------------------------------------------------
// Purpose: Get a pointer to the dag node the constraint is controlling. For
// the ik constraint this is the start joint, but both the start and the mid
// joint are actually 'slaves' of the constraint.
//-----------------------------------------------------------------------------
const CDmeDag *CDmeRigIKConstraintOperator::GetSlave() const
{
return m_StartJoint->GetDag();
}
//-----------------------------------------------------------------------------
// Purpose: Determine if the the constraint has slave with the specified name,
// for the ik constraint this is always false.
//-----------------------------------------------------------------------------
bool CDmeRigIKConstraintOperator::IsSlaveObject( char const *pchName ) const
{
// IK Handle doesn't have targets that can be added
return false;
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigTwistSlave
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Expose the CDmeConstraintTarget class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigTwistSlave, CDmeRigTwistSlave );
//-----------------------------------------------------------------------------
// Purpose: Perform initialization operations.
//-----------------------------------------------------------------------------
void CDmeRigTwistSlave::OnConstruction()
{
m_flWeight.Init( this, "weight" );
}
//-----------------------------------------------------------------------------
// Purpose: Perform shutdown operations
//-----------------------------------------------------------------------------
void CDmeRigTwistSlave::OnDestruction()
{
}
//-------------------------------------------------------------------------------------------------
//
// CDmeRigTwistConstraintOperator
//
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Expose the CDmeConstraintTarget class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeRigTwistConstraintOperator, CDmeRigTwistConstraintOperator );
//-----------------------------------------------------------------------------
// Purpose: Perform initialization operations.
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::OnConstruction()
{
m_bInverse.Init( this, "inverse" );
m_vUpAxis.InitAndSet( this, "upVector", Vector( 0.0f, 1.0f, 0.0f ) );
m_flWeights.Init( this, "weights" );
m_eSlaves.Init( this, "twistJoints" );
m_qParentBindRotation.InitAndSet( this, "parentBindRotation", quat_identity );
m_qChildBindRotation.InitAndSet( this, "childBindRotation", quat_identity );
}
//-----------------------------------------------------------------------------
// Purpose: Perform shutdown operations
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::OnDestruction()
{
ClearSlaves();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmeRigTwistConstraintOperator::SetTargets( CDmeDag *pDmeDagParent, CDmeDag *pDmeDagChild )
{
ClearHandles();
if ( !pDmeDagParent || !pDmeDagChild )
return false;
CDmeDag *dmeDags[] = { pDmeDagParent, pDmeDagChild };
float flWeights[] = { 1.0f, 1.0f };
AddHandles( 2, dmeDags, flWeights, false, NULL );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Return the DmeDag parent target or NULL
// m_Targets[0] is the parent target
//-----------------------------------------------------------------------------
CDmeDag *CDmeRigTwistConstraintOperator::GetParentTarget() const
{
return m_Targets.Count() >= 1 ? m_Targets[0]->GetDag() : NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Return the DmeDag child target or NULL
// m_Targets[0] is the parent target
//-----------------------------------------------------------------------------
CDmeDag *CDmeRigTwistConstraintOperator::GetChildTarget() const
{
return m_Targets.Count() >= 2 ? m_Targets[1]->GetDag() : NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Remove all slaves
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::ClearSlaves()
{
for ( int i = 0; i < m_eSlaves.Count(); ++i )
{
CDmeConstraintSlave *pDmeConstraintSlave = m_eSlaves[i];
if ( !pDmeConstraintSlave )
continue;
DestroyElement( pDmeConstraintSlave );
}
}
//-----------------------------------------------------------------------------
// Purpose: Add the specified DmeDag as a slave with the specified weight
// Returns: The index of the slave, -1 on failure
//-----------------------------------------------------------------------------
int CDmeRigTwistConstraintOperator::AddSlave( CDmeDag *pDmeDagSlave, float flWeight )
{
CDmeRigTwistSlave *pDmeRigTwistSlave = CreateElement< CDmeRigTwistSlave >( pDmeDagSlave->GetName(), GetFileId() );
if ( !pDmeRigTwistSlave )
return -1;
// This assume the current rotation of the specified dag is the base/bind rotation
matrix3x4_t mBind;
pDmeDagSlave->GetLocalMatrix( mBind );
Quaternion qBind;
MatrixQuaternion( mBind, qBind );
pDmeRigTwistSlave->SetDag( pDmeDagSlave );
pDmeRigTwistSlave->SetWeight( flWeight );
pDmeRigTwistSlave->SetBaseOrientation( qBind );
return m_eSlaves.AddToTail( pDmeRigTwistSlave );
}
//-----------------------------------------------------------------------------
// Purpose: Return the DmeDag of the slave at the specified index
//-----------------------------------------------------------------------------
CDmeDag *CDmeRigTwistConstraintOperator::GetSlaveDag( int i ) const
{
return m_eSlaves.Count() > i ? m_eSlaves[i]->GetDag() : NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Return the weight of the slave at the specified index
//-----------------------------------------------------------------------------
float CDmeRigTwistConstraintOperator::GetSlaveWeight( int i ) const
{
return m_eSlaves.Count() > i ? m_eSlaves[i]->GetWeight() : 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Set the bind orientation of the parent
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::SetParentBindRotation( const Quaternion &qBindRotation )
{
m_qParentBindRotation = qBindRotation;
}
//-----------------------------------------------------------------------------
// Purpose: Set the bind orientation of the child
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::SetChildBindRotation( const Quaternion &qBindRotation )
{
m_qChildBindRotation = qBindRotation;
}
//-----------------------------------------------------------------------------
// Purpose: Return the bind orientation of the slave at the specified index
//-----------------------------------------------------------------------------
const Quaternion &CDmeRigTwistConstraintOperator::GetSlaveBindOrientation( int i ) const
{
if ( m_eSlaves.Count() > i )
{
return m_eSlaves[i]->GetBaseOrientation();
}
else
{
return quat_identity;
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the bind orientation of the slave at the specified index
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::SetSlaveBindOrientation( const Quaternion &qBindRotation, int i )
{
if ( m_eSlaves.Count() > i )
{
m_eSlaves[i]->SetBaseOrientation( qBindRotation );
}
}
//-----------------------------------------------------------------------------
// Purpose: Compute the twist
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::Operate()
{
VPROF_BUDGET( "CDmeRigTwistConstraintOperator::Operate", "SFM" );
/*
* TODO: Call the Twist constraint code
* But it needs to be moved into bonesetup first
* Right now this is just a data container
*/
}
//-----------------------------------------------------------------------------
// Purpose: Get the attributes which are written to by the constraint.
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
for ( int i = 0; i < m_eSlaves.Count(); ++i )
{
CDmeConstraintSlave *pDmeConstraintSlave = m_eSlaves[i];
if ( !pDmeConstraintSlave )
continue;
AddAttribute( attrs, AA_TYPE_ORIENTATION, pDmeConstraintSlave->GetTransform() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Compute the offset for the specified target. The twist constraint
// does not use target offsets, so this function simply clears the offset for the
// specified target.
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::ComputeOffset( Vector &vOffset, Quaternion &qOffset, CDmeConstraintTarget *pTarget, bool bPreserveOffset )
{
// No target offset allowed
vOffset = vec3_origin;
qOffset = quat_identity;
}
//-----------------------------------------------------------------------------
// Purpose: Disconnect the channels driving the slave dag nodes from the dag
// transforms and connect them to the constraint
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::DisconnectTransformChannels()
{
for ( int i = 0; i < m_eSlaves.Count(); ++i )
{
CDmeConstraintSlave *pDmeConstraintSlave = m_eSlaves[i];
if ( !pDmeConstraintSlave )
continue;
DisconnectSlaveChannels( pDmeConstraintSlave, AA_TYPE_ORIENTATION );
}
}
//-----------------------------------------------------------------------------
// Purpose: Reconnect the base channels of each slave directly to the dag
//-----------------------------------------------------------------------------
void CDmeRigTwistConstraintOperator::ReconnectTransformChannels()
{
for ( int i = 0; i < m_eSlaves.Count(); ++i )
{
CDmeConstraintSlave *pDmeConstraintSlave = m_eSlaves[i];
if ( !pDmeConstraintSlave )
continue;
ReconnectSlaveChannels( pDmeConstraintSlave, AA_TYPE_ORIENTATION );
}
}
//-----------------------------------------------------------------------------
// Purpose: Get a pointer to the dag node the constraint is controlling.
// Return the first
//-----------------------------------------------------------------------------
const CDmeDag *CDmeRigTwistConstraintOperator::GetSlave() const
{
return m_eSlaves.Count() ? m_eSlaves[0]->GetDag() : NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Determine if the the constraint has slave with the specified name
//-----------------------------------------------------------------------------
bool CDmeRigTwistConstraintOperator::IsSlaveObject( char const *pchName ) const
{
for ( int i = 0; i < m_eSlaves.Count(); ++i )
{
CDmeConstraintSlave *pDmeConstraintSlave = m_eSlaves[i];
if ( !pDmeConstraintSlave )
continue;
if ( !V_stricmp( pchName, pDmeConstraintSlave->GetName() ) )
return true;
}
return false;
}