|
|
//====== 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; }
|