csgo-2018-source/dmeutils/dmanimutils.cpp

2141 lines
78 KiB
C++
Raw Normal View History

2021-07-25 12:11:47 +08:00
//====== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. =======
//
// Implementation of CDmAnimUtils, a set of animation related utilities
// which work on DmeDag and other DmElement derived objects.
//
//=============================================================================
#include "dmeutils/dmanimutils.h"
#include "movieobjects/dmetransformcontrol.h"
#include "movieobjects/dmegamemodel.h"
#include "movieobjects/dmetimeselection.h"
#include "movieobjects/dmetrack.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------------------------------------------------------
// Create an infinite time selection
//-------------------------------------------------------------------------------------------------
CDmeTimeSelection *CDmAnimUtils::CreateInfiniteTimeSelection()
{
CDmeTimeSelection *pTimeSelection = CreateElement< CDmeTimeSelection >( "AnimUtilsTimeSelection", DMFILEID_INVALID );
pTimeSelection->SetInfinite( 0 );
pTimeSelection->SetInfinite( 1 );
return pTimeSelection;
}
//-------------------------------------------------------------------------------------------------
// Create an absolute time selection with the specified key times
//-------------------------------------------------------------------------------------------------
CDmeTimeSelection *CDmAnimUtils::CreateTimeSelection( DmeTime_t leftFalloff, DmeTime_t leftHold, DmeTime_t rightHold, DmeTime_t rightFalloff )
{
CDmeTimeSelection *pTimeSelection = CreateElement< CDmeTimeSelection >( "AnimUtilsTimeSelection", DMFILEID_INVALID );
pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_LEFT_FALLOFF, leftFalloff );
pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_LEFT_HOLD, leftHold );
pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_RIGHT_HOLD, rightHold );
pTimeSelection->SetAbsTime( DMETIME_ZERO, TS_RIGHT_FALLOFF, rightFalloff );
return pTimeSelection;
}
//-------------------------------------------------------------------------------------------------
// Create a CDmeDag instance with the specified name with the specified position and orientation
//-------------------------------------------------------------------------------------------------
CDmeDag *CDmAnimUtils::CreateDag( char const *pchHandleName, const Vector &position, const Quaternion &orientation, CDmeDag *pParent )
{
DmFileId_t fileId = DMFILEID_INVALID;
if ( pParent )
{
fileId = pParent->GetFileId();
}
// Create the dag element
CDmeDag *pDag = CreateElement< CDmeDag >( pchHandleName, fileId );
if ( pDag )
{
// Add the handle to the scene
if ( pParent )
{
pParent->AddChild( pDag );
}
// Position the handle
pDag->SetAbsPosition( position );
pDag->SetAbsOrientation( orientation );
}
return pDag;
}
//-------------------------------------------------------------------------------------------------
// Get the average position of the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
Vector CDmAnimUtils::GetDagPosition( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
{
Vector position = vec3_origin;
Quaternion orientation = quat_identity;
GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
return position;
}
//-------------------------------------------------------------------------------------------------
// Get the average Euler rotation of the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
Vector CDmAnimUtils::GetDagRotation( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
{
Vector position = vec3_origin;
Quaternion orientation = quat_identity;
GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
// Convert the quaternion to Euler rotation
RadianEuler eulerRotation( orientation );
Vector rotation = vec3_origin;
rotation.x = RAD2DEG( eulerRotation.x );
rotation.y = RAD2DEG( eulerRotation.y );
rotation.z = RAD2DEG( eulerRotation.z );
return rotation;
}
//-------------------------------------------------------------------------------------------------
// Get the average orientation (quaternion) of the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
Quaternion CDmAnimUtils::GetDagOrientation( const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReferenceDag )
{
Vector position = vec3_origin;
Quaternion orientation = quat_identity;
GetDagPositionOrienation( position, orientation, dagList, space, pReferenceDag );
return orientation;
}
//-------------------------------------------------------------------------------------------------
// Purpose: Get the average position and orientation of the provided dag nodes in the specified
// space
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GetDagPositionOrienation( Vector &position, Quaternion &orienation, const CUtlVector< CDmeDagPtr > &dagList, TransformSpace_t space, const CDmeDag *pReference )
{
int nDagNodes = dagList.Count();
position = vec3_origin;
orienation = quat_identity;
// Return the origin if there are no nodes provided
if ( nDagNodes < 1 )
return;
// Sum the positions of all of the provided dag nodes
// and add the orientation of each node to the list.
CUtlVector< Quaternion > orientationList;
orientationList.EnsureCapacity( nDagNodes );
float flDagCount = 0;
const CDmeDag *pPrimaryDag = NULL;
Vector positionSum = vec3_origin;
for ( int iNode = 0; iNode < nDagNodes; ++iNode )
{
const CDmeDag *pDagNode = dagList[ iNode ];
if ( pDagNode == NULL )
continue;
// Get the world space position of the dag
matrix3x4_t absTransform;
Vector worldPos;
Quaternion worldOrientation;
pDagNode->GetAbsTransform( absTransform );
MatrixAngles( absTransform, worldOrientation, worldPos );
positionSum += worldPos;
orientationList.AddToTail( worldOrientation );
flDagCount += 1.0f;
pPrimaryDag = pDagNode;
}
// Stop if there were no valid dag nodes
if ( pPrimaryDag == NULL )
return;
// Calculate the average position in world space
Vector positionAvg = positionSum / flDagCount;
// Calculate the average orienation in world space
Quaternion orienationAvg;
QuaternionAverageExponential( orienationAvg, orientationList.Count(), orientationList.Base() );
// Find the dag node whose space the position is to be returned in
const CDmeDag *pSpaceDag = NULL;
switch ( space )
{
case TS_LOCAL_SPACE:
pSpaceDag = pPrimaryDag->GetParent();
break;
case TS_OBJECT_SPACE:
pSpaceDag = pPrimaryDag;
break;
case TS_REFERENCE_SPACE:
pSpaceDag = pReference;
break;
default:
break;
}
// Convert the average world space position to the space of the specified dag node
if ( pSpaceDag )
{
matrix3x4_t worldToLocal;
matrix3x4_t localToWorld;
pSpaceDag->GetAbsTransform( localToWorld );
MatrixInvert( localToWorld, worldToLocal );
VectorTransform( positionAvg, worldToLocal, position );
float rotationAngle;
Vector wsRotationAxis;
Vector lsRotationAxis;
QuaternionAxisAngle( orienationAvg, wsRotationAxis, rotationAngle );
VectorRotate( wsRotationAxis, worldToLocal, lsRotationAxis );
AxisAngleQuaternion( lsRotationAxis, rotationAngle, orienation );
}
else
{
position = positionAvg;
orienation = orienationAvg;
}
}
//-------------------------------------------------------------------------------------------------
// Move the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::MoveDagNodes( const Vector &offset, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag *pReferenceDag )
{
TransformDagNodes( offset, quat_identity, dagList, bRelative, space, pReferenceDag, true, false );
}
//-------------------------------------------------------------------------------------------------
// Move the provided dag nodes in the specified space and apply the operation to the logs
// associated with the dag
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::MoveDagNodes( const Vector &offset, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag *pReferenceDag )
{
TransformDagNodes( offset, quat_identity, dagList, bRelative, pTimeSelection, bOffsetOverTime, space, pReferenceDag, true, false );
}
//-------------------------------------------------------------------------------------------------
// Rotate the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::RotateDagNodes( const Vector &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag* pReferenceDag )
{
// Rotate the selected dag nodes in the specified space
RadianEuler eulerRotation( DEG2RAD( rotation.x ), DEG2RAD( rotation.y ), DEG2RAD( rotation.z ) );
Quaternion qRotation( eulerRotation );
TransformDagNodes( vec3_origin, qRotation, dagList, bRelative, space, pReferenceDag, false, true );
}
//-------------------------------------------------------------------------------------------------
// Rotate the provided dag nodes in the specified space and apply the operation to the logs
// associated with the dag
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::RotateDagNodes( const Vector &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag* pReferenceDag )
{
// Rotate the selected dag nodes in the specified space
RadianEuler eulerRotation( DEG2RAD( rotation.x ), DEG2RAD( rotation.y ), DEG2RAD( rotation.z ) );
Quaternion qRotation( eulerRotation );
TransformDagNodes( vec3_origin, qRotation, dagList, bRelative, pTimeSelection, bOffsetOverTime, space, pReferenceDag, false, true );
}
//-------------------------------------------------------------------------------------------------
// Translate an rotate the provided dag nodes in the specified space
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::TransformDagNodes( const Vector &offset, const Quaternion &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, TransformSpace_t space, CDmeDag* pReferenceDag, bool bPosition, bool bRotation )
{
float rotationAngle;
Vector rotationAxis;
matrix3x4_t orienationMat;
QuaternionAxisAngle( rotation, rotationAxis, rotationAngle );
QuaternionMatrix( rotation, orienationMat );
int nDagNodes = dagList.Count();
for ( int iNode = 0; iNode < nDagNodes; ++iNode )
{
const CDmeDag *pDagNode = dagList[ iNode ];
if ( pDagNode == NULL )
continue;
// The value stored in the dag node's transform is in local space ( space of the parent ),
// we must convert the offset from the space it is provided in to a value in local space.
Vector lsOffset;
Vector lsRotationAxis;
matrix3x4_t lsOrientationMat;
matrix3x4_t objectToLocal;
pDagNode->GetLocalMatrix( objectToLocal );
if ( space == TS_LOCAL_SPACE )
{
// The offset and rotation are provided in local space, nothing needs to be done
lsOffset = offset;
lsRotationAxis = rotationAxis;
lsOrientationMat = orienationMat;
}
else if ( space == TS_OBJECT_SPACE )
{
// The offset and rotation are provided in object space, it is simply transformed
// by the object's transform to get it into space of the object's parent.
VectorTransform( offset, objectToLocal, lsOffset );
VectorRotate( rotationAxis, objectToLocal, lsRotationAxis );
ConcatTransforms( orienationMat, objectToLocal, lsOrientationMat);
}
else
{
// Calculate the offset to be applied in world space
Vector wsOffset = offset;
Vector wsRotationAxis = rotationAxis;
matrix3x4_t wsOrientationMat = orienationMat;
if ( pReferenceDag && ( space == TS_REFERENCE_SPACE ) )
{
matrix3x4_t referenceToWorld;
pReferenceDag->GetAbsTransform( referenceToWorld );
VectorTransform( offset, referenceToWorld, wsOffset );
VectorRotate( rotationAxis, referenceToWorld, wsRotationAxis );
ConcatTransforms( orienationMat, referenceToWorld, wsOrientationMat );
}
// Calculate the offset to be applied to the dag's local space
CDmeDag *pParent = pDagNode->GetParent();
if ( pParent )
{
matrix3x4_t localToWorld;
matrix3x4_t worldToLocal;
pParent->GetAbsTransform( localToWorld );
MatrixInvert( localToWorld, worldToLocal );
VectorRotate( wsRotationAxis, worldToLocal, lsRotationAxis );
MatrixMultiply( wsOrientationMat, worldToLocal, lsOrientationMat );
if ( bRelative )
{
VectorRotate( wsOffset, worldToLocal, lsOffset );
}
else
{
VectorTransform( wsOffset, worldToLocal, lsOffset );
}
}
else
{
lsOffset = wsOffset;
lsRotationAxis = wsRotationAxis;
lsOrientationMat = wsOrientationMat;
}
}
// Now that we have the offset and rotation in local space, they may be applied to the
// local transform. They may be applied as an absolute position and orientation, or a
// relative offset and rotation.
Quaternion lsRotation;
AxisAngleQuaternion( lsRotationAxis, rotationAngle, lsRotation );
Vector lsPosition = lsOffset;
Quaternion lsOrientation = quat_identity;
if ( bRelative )
{
Vector lsOrigin;
MatrixAngles( objectToLocal, lsOrientation, lsOrigin );
QuaternionMult( lsRotation, lsOrientation, lsOrientation );
lsPosition = lsOrigin + lsOffset;
}
else
{
MatrixQuaternion( lsOrientationMat, lsOrientation );
}
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( pTransform )
{
if ( bPosition )
{
pTransform->SetPosition( lsPosition );
}
if ( bRotation )
{
pTransform->SetOrientation( lsOrientation );
}
}
// Find the position and orientation controls for the transform of the dag node.
CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
// Assign the new local space position and orienation to the control
if ( pTransformControl )
{
if ( bPosition )
{
pTransformControl->SetPosition( lsPosition );
}
if ( bRotation)
{
pTransformControl->SetOrientation( lsOrientation );
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Perform both a translation and a rotation of the specified dag nodes and apply the operation to
// the logs associated with the dag
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::TransformDagNodes( const Vector &offset, const Quaternion &rotation, const CUtlVector< CDmeDagPtr > &dagList, bool bRelative, const CDmeTimeSelection *pTimeSelection, bool bOffsetOverTime, TransformSpace_t space, CDmeDag* pReferenceDag, bool bPosition, bool bRotation )
{
TransformDagNodes( offset, rotation, dagList, bRelative, space, pReferenceDag, bPosition, bRotation );
if ( pTimeSelection )
{
// Now apply the operation through time
DmeLog_TimeSelection_t logTimeSelection;
logTimeSelection.m_bAttachedMode = pTimeSelection->IsRelative();
logTimeSelection.m_flThreshold = pTimeSelection->GetThreshold();
logTimeSelection.m_nFalloffInterpolatorTypes[ 0 ] = pTimeSelection->GetFalloffInterpolatorType( 0 );
logTimeSelection.m_nFalloffInterpolatorTypes[ 1 ] = pTimeSelection->GetFalloffInterpolatorType( 1 );
logTimeSelection.m_nResampleInterval = pTimeSelection->GetResampleInterval();
logTimeSelection.m_bInfinite[ 0 ] = pTimeSelection->IsInfinite( 0 );
logTimeSelection.m_bInfinite[ 1 ] = pTimeSelection->IsInfinite( 1 );
if ( logTimeSelection.m_bInfinite[ 0 ] )
{
logTimeSelection.m_nTimes[ TS_LEFT_FALLOFF ] = DMETIME_MAXTIME;
logTimeSelection.m_nTimes[ TS_LEFT_HOLD ] = DMETIME_MAXTIME;
}
else
{
logTimeSelection.m_nTimes[ TS_LEFT_FALLOFF ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_LEFT_FALLOFF );
logTimeSelection.m_nTimes[ TS_LEFT_HOLD ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_LEFT_HOLD );
}
if ( logTimeSelection.m_bInfinite[ 1 ] )
{
logTimeSelection.m_nTimes[ TS_RIGHT_FALLOFF ] = DMETIME_MINTIME;
logTimeSelection.m_nTimes[ TS_RIGHT_HOLD ] = DMETIME_MINTIME;
}
else
{
logTimeSelection.m_nTimes[ TS_RIGHT_HOLD ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_RIGHT_HOLD );
logTimeSelection.m_nTimes[ TS_RIGHT_FALLOFF ] = pTimeSelection->GetAbsTime( DMETIME_ZERO, TS_RIGHT_FALLOFF );
}
// Apply the results to the channels of the selected dag nodes
if ( bOffsetOverTime )
{
logTimeSelection.m_TransformWriteMode = TRANSFORM_WRITE_MODE_OFFSET;
logTimeSelection.SetRecordingMode( RECORD_ATTRIBUTESLIDER );
}
OperateDagChannels( dagList, CM_RECORD, logTimeSelection, NULL, NULL );
}
}
//-------------------------------------------------------------------------------------------------
// Set the current position and orientation as the defaults for the specified dag nodes.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::SetDagTransformDefaults( const CUtlVector< CDmeDagPtr > &dagList, bool bPosition, bool bOrientation )
{
if ( ( bPosition == false ) && ( bOrientation == false ) )
return;
int nDagNodes = dagList.Count();
for ( int iDagNode = 0; iDagNode < nDagNodes; ++iDagNode )
{
const CDmeDag *pDagNode = dagList[ iDagNode ];
if ( pDagNode == NULL )
continue;
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( pTransform == NULL )
continue;
CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
if ( pTransformControl )
{
if ( bPosition )
{
const Vector &position = pTransform->GetPosition();
pTransformControl->SetDefaultPosition( position );
}
if ( bOrientation )
{
const Quaternion &orientation = pTransform->GetOrientation();
pTransformControl->SetDefaultOrientation( orientation );
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Set all of the controls targeting bones in the animation set to the reference pose
// position and orientation
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::SetReferencePose( CDmeGameModel *pGameModel, const CUtlVector< CDmeDag* > &dagList )
{
if ( pGameModel == NULL )
return;
int nDagNodes = dagList.Count();
for ( int iDagNode = 0; iDagNode < nDagNodes; ++iDagNode )
{
const CDmeDag *pDagNode = dagList[ iDagNode ];
if ( pDagNode == NULL )
continue;
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( pTransform == NULL )
continue;
// Find the bone corresponding to the control
int boneIndex = pGameModel->FindBone( pTransform );
// Skip the dag if there is no corresponding bone unless it is the root
if ( ( boneIndex < 0 ) && ( pDagNode != pGameModel ) )
continue;
CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
if ( pTransformControl )
{
Vector position = vec3_origin;
pGameModel->GetBoneDefaultPosition( boneIndex, position );
pTransformControl->SetPosition( position );
Quaternion orientation = quat_identity;
pGameModel->GetBoneDefaultOrientation( boneIndex, orientation );
pTransformControl->SetOrientation( orientation );
}
}
}
//-------------------------------------------------------------------------------------------------
// Update the default values for the position and orientation controls of the specified dag so they
// maintain their world space position
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::UpdateDefaultsForNewParent( CDmeDag *pDagNode, CDmeDag *pParentDag )
{
// Calculate the transform needed to convert from the
// space of the current parent to the new parent.
matrix3x4_t worldToNewParent;
matrix3x4_t defaultWorldToNewParent;
if ( pParentDag )
{
matrix3x4_t parentToWorld;
pParentDag->GetAbsTransform( parentToWorld );
MatrixInvert( parentToWorld, worldToNewParent );
matrix3x4_t defaultParentToWorld;
GetDefaultAbsTransform( pParentDag, defaultParentToWorld );
MatrixInvert( defaultParentToWorld, defaultWorldToNewParent );
}
else
{
SetIdentityMatrix( worldToNewParent );
SetIdentityMatrix( defaultWorldToNewParent );
}
matrix3x4_t oldToNewParentMat;
matrix3x4_t defaultOldToNewParentMat;
CDmeDag *pOldParent = pDagNode->GetParent();
if ( pOldParent )
{
matrix3x4_t oldParentToWorld;
pOldParent->GetAbsTransform( oldParentToWorld );
ConcatTransforms( worldToNewParent, oldParentToWorld, oldToNewParentMat );
matrix3x4_t defaultOldParentToWorld;
GetDefaultAbsTransform( pOldParent, defaultOldParentToWorld);
ConcatTransforms( defaultWorldToNewParent, defaultOldParentToWorld, defaultOldToNewParentMat );
}
else
{
oldToNewParentMat = worldToNewParent;
defaultOldToNewParentMat = defaultWorldToNewParent;
}
CDmeTransformControl *pTransformControl = pDagNode->FindTransformControl();
if ( pTransformControl )
{
// Move the position value into the space of the new parent
Vector position = pTransformControl->GetPosition();
Vector newPosition;
VectorTransform( position, oldToNewParentMat, newPosition );
pTransformControl->SetPosition( newPosition );
// Move the default position into the space of new parent
if ( pTransformControl->HasDefaultPosition() )
{
Vector newPosition;
Vector position = pTransformControl->GetDefaultPosition();
VectorTransform( position, defaultOldToNewParentMat, newPosition );
pTransformControl->SetDefaultPosition( newPosition );
}
// Move the orientation value into the space of the new parent
Quaternion orientation = pTransformControl->GetOrientation();
Quaternion newOrientation;
matrix3x4_t oldOrientMat, newOrientMat;
QuaternionMatrix( orientation, oldOrientMat );
ConcatTransforms( oldToNewParentMat, oldOrientMat, newOrientMat );
MatrixQuaternion( newOrientMat, newOrientation );
pTransformControl->SetOrientation( newOrientation );
// Move the default orientation into the space of the new parent
if ( pTransformControl->HasDefaultOrientation() )
{
Quaternion orientation = pTransformControl->GetDefaultOrientation();
Quaternion newOrientation;
matrix3x4_t oldOrientMat, newOrientMat;
QuaternionMatrix( orientation, oldOrientMat );
ConcatTransforms( defaultOldToNewParentMat, oldOrientMat, newOrientMat );
MatrixQuaternion( newOrientMat, newOrientation );
pTransformControl->SetDefaultOrientation( newOrientation );
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Re-parent the specified dag node from its current parent to the specified dag node.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::ReParentDagNode( CDmeDag *pDagNode, CDmeDag *pParentDag, bool bMaintainWorldPos, ReParentLogMode_t logMode )
{
if ( pDagNode == NULL )
return;
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( bMaintainWorldPos && pTransform )
{
// Modify the default values of the controls on the dag so that they maintain
// their world space position when the specified dag node is made the new parent.
UpdateDefaultsForNewParent( pDagNode, pParentDag );
// Get the current world space matrix of the dag
matrix3x4_t currentWorldMat;
pDagNode->GetAbsTransform( currentWorldMat );
// Get the current world space matrix of the new parent
matrix3x4_t parentWorldMat;
if ( pParentDag )
{
pParentDag->GetAbsTransform( parentWorldMat );
}
else
{
SetIdentityMatrix( parentWorldMat );
}
matrix3x4_t invParentMat;
MatrixInvert( parentWorldMat, invParentMat );
// If an update of the logs is requested re-generate the local logs of the dag so that the same
// world space location is maintained once the dag is a child of the specified parent.
if ( logMode == REPARENT_LOGS_MAINTAIN_WORLD )
{
// Determine if the dag has existing position and orientation samples
CDmeChannel *pPosChannel = FindDagTransformChannel( pDagNode, TRANSFORM_POSITION );
CDmeChannel *pRotChannel = FindDagTransformChannel( pDagNode, TRANSFORM_ORIENTATION );
// Generate the log samples in the space of the new parent
GenerateLogSamples( pDagNode, pParentDag, pPosChannel != NULL, pRotChannel != NULL, NULL );
}
else if ( logMode == REPARENT_LOGS_OFFSET_LOCAL )
{
// Compute the transform from the space defined by the
// current parent to the space defined by the new parent.
matrix3x4_t currentParentMat;
pDagNode->GetParentWorldMatrix( currentParentMat );
matrix3x4_t oldToNewParentMat;
ConcatTransforms( invParentMat, currentParentMat, oldToNewParentMat );
// Apply the transform to all the samples of the position channel of the dag
CDmeChannel *pPosChannel = FindDagTransformChannel( pDagNode, TRANSFORM_POSITION );
if ( pPosChannel )
{
CDmeTypedLogLayer< Vector > *pPositionLog = CastElement< CDmeTypedLogLayer< Vector > >( pPosChannel->GetLog() );
RotatePositionLog( pPositionLog, oldToNewParentMat );
}
// Apply the the transform to all the samples of the orientation channel of the dag
CDmeChannel *pRotChannel = FindDagTransformChannel( pDagNode, TRANSFORM_ORIENTATION );
if ( pRotChannel )
{
CDmeTypedLogLayer< Quaternion > *pRotationLog = CastElement< CDmeTypedLogLayer< Quaternion > >( pRotChannel->GetLog() );
RotateOrientationLog( pRotationLog, oldToNewParentMat, true );
}
}
// Compute the new local matrix of the transform which will maintain the world space position of the dag
matrix3x4_t newLocalMat;
ConcatTransforms( invParentMat, currentWorldMat, newLocalMat );
pTransform->SetTransform( newLocalMat );
}
// Remove the child from all parents
CUtlVector< CDmeDag* > parents;
FindAncestorsReferencingElement( pDagNode, parents );
int nParents = parents.Count();
for ( int i = 0; i < nParents; ++i )
{
parents[ i ]->RemoveChild( pDagNode );
}
// Add the child to the specified parent
if ( pParentDag )
{
pParentDag->AddChild( pDagNode );
}
if ( logMode == REPARENT_LOGS_OVERWRITE )
{
SetLogsToCurrentTransform( pDagNode );
}
}
//-------------------------------------------------------------------------------------------------
// Set the temporary override parent of a dag, maintaining its world space position and orientation
// animation
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::SetOverrideParent( CDmeDag *pDagNode, const CDmeDag *pOverrideParent, bool bPosition, bool bRotation )
{
if ( pDagNode == NULL )
return;
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( pTransform == NULL )
return;
// Do not allow the override parent to be set to a dag that is a child of the specified dag.
if ( pDagNode->IsAncestorOfDag( pOverrideParent ) || ( pDagNode == pOverrideParent ) )
return;
// If both position and rotation are false treat it the same as if pOverrideParent is NULL.
if ( !bPosition && !bRotation )
{
pOverrideParent = NULL;
}
// Get the shot and movie that the specified dag node belongs to for use in time conversions
CDmeClip *pShot = NULL;
CDmeClip *pMovie = NULL;
FindShotAndMovieForDag( pDagNode, pShot, pMovie );
// Find the channels which will be needed to evaluate
// the position of the child and the parent dag.
CUtlVector< CDmeOperator* > operatorList;
pDagNode->FindRelevantOperators( operatorList );
if ( pOverrideParent )
{
pOverrideParent->FindRelevantOperators( operatorList );
}
// Build a list of all of the key times for the operators
CUtlVector< DmeTime_t > keyTimes;
CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
// Get the world space transform of the dag at all the key times
CUtlVector< matrix3x4_t > worldSpaceTransforms;
GenerateDagWorldTransformList( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
// Get the current world space matrix of the dag
matrix3x4_t currentWorldMat;
pDagNode->GetAbsTransform( currentWorldMat );
// Now set the temporary override parent, note if NULL
// is specified the override will simply be removed.
pDagNode->SetOverrideParent( pOverrideParent, bPosition, bRotation );
// Update the logs of the dag node so that it matches
// its previous world space transform at all times
SetDagWorldSpaceTransforms( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
// Compute the new local matrix of the transform which
// will maintain the world space position of the dag
pDagNode->SetAbsTransform( currentWorldMat );
}
//-------------------------------------------------------------------------------------------------
// Enable or disable the override parent functionality on a dag, if different than the current
// state the logs will be updated such the dag world space position and orientation are maintained.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::ToggleOverrideParent( CDmeDag *pDagNode, bool bEnable )
{
if ( pDagNode == NULL )
return;
CDmeTransform *pTransform = pDagNode->GetTransform();
if ( pTransform == NULL )
return;
// If the enable state matches the requested state nothing needs to be done.
if ( pDagNode->IsOverrideParentEnabled() == bEnable )
return;
// Get the current override parent regardless of the override state
const CDmeDag *pOverrideParent = pDagNode->GetOverrideParent( true );
if ( pOverrideParent == NULL )
return;
// Get the shot and movie that the specified dag node belongs to for use in time conversions
CDmeClip *pShot = NULL;
CDmeClip *pMovie = NULL;
FindShotAndMovieForDag( pDagNode, pShot, pMovie );
// Find the channels which will be needed to evaluate
// the position of the child and the parent dag.
CUtlVector< CDmeOperator* > operatorList;
pDagNode->FindRelevantOperators( operatorList );
if ( pOverrideParent )
{
pOverrideParent->FindRelevantOperators( operatorList );
}
// Build a list of all of the key times for the operators
CUtlVector< DmeTime_t > keyTimes;
CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
// Get the world space transform of the dag at all the key times
CUtlVector< matrix3x4_t > worldSpaceTransforms;
GenerateDagWorldTransformList( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
// Get the current world space matrix of the dag
matrix3x4_t currentWorldMat;
pDagNode->GetAbsTransform( currentWorldMat );
// Now change the override parent enable state and update
// the local transforms to match the world transforms
pDagNode->EnableOverrideParent( bEnable );
// Update the logs of the dag node so that it matches
// its previous world space transform at all times
SetDagWorldSpaceTransforms( pDagNode, keyTimes, worldSpaceTransforms, pShot, pMovie );
// Compute the new local matrix of the transform which
// will maintain the world space position of the dag
pDagNode->SetAbsTransform( currentWorldMat );
}
//-------------------------------------------------------------------------------------------------
// Determine if the specified dag node has any constraints
//-------------------------------------------------------------------------------------------------
bool CDmAnimUtils::DagHasConstraints( CDmeDag *pDag )
{
if ( pDag != NULL )
{
// Find the constraint slaves referencing this dag
CUtlVector< CDmeConstraintSlave* > constraintSlaves( 0, 8 );
FindAncestorsReferencingElement( pDag, constraintSlaves );
int nSlaves = constraintSlaves.Count();
for ( int iSlave = 0; iSlave < nSlaves; ++iSlave )
{
CDmeRigBaseConstraintOperator *pConstraint = FindAncestorReferencingElement< CDmeRigBaseConstraintOperator >( constraintSlaves[ iSlave ] );
if ( pConstraint )
return true;
}
}
return false;
}
//-------------------------------------------------------------------------------------------------
// Remove all constraints from the currently selected dag nodes.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::RemoveConstraints( CDmeDag *pDag )
{
CDmeRigBaseConstraintOperator::RemoveConstraintsFromDag( pDag );
}
//-------------------------------------------------------------------------------------------------
// Create a constraint of the specified type
//-------------------------------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmAnimUtils::CreateConstraint( const char *pchName, EConstraintType constraintType, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetList, bool bPreserveOffset, float flTargetWeight, bool bOperate )
{
// Verify the input parameters are valid.
if ( ( pConstrainedDag == NULL ) || ( targetList.Count() <= 0 ) )
return NULL;
// IK constraints should not be created through this path
// since their setup is significantly different
if ( constraintType == CT_IK )
return NULL;
// First see if there is already an existing constraint of the specified type on the dag node.
CDmeRigBaseConstraintOperator *pConstraint = FindConstraintOnDag( pConstrainedDag, constraintType );
// Create a constraint if one of the specified type did not already exist on the dag node.
if ( pConstraint == NULL )
{
pConstraint = InstanceConstraint( pchName, constraintType, pConstrainedDag->GetFileId() );
if ( pConstraint == NULL )
return NULL;
// Set the slave dag which will be controlled by the constraint
pConstraint->SetSlave( pConstrainedDag );
// Disconnect the channels from the attributes of slave transform that are written by the constraint.
pConstraint->DisconnectTransformChannels();
}
// Initialize the weights
int nTargets = targetList.Count();
CUtlVector< float > weights;
weights.SetCount( nTargets );
for ( int i = 0; i < nTargets; ++i )
{
weights[ i ] = flTargetWeight;
}
// Add the handles to the constraint or update their weights if they already exist.
pConstraint->AddHandles( nTargets, targetList.Base(), weights.Base(), bPreserveOffset, NULL );
// If specified operate the constraint so that its result is available immediately
if ( bOperate )
{
pConstraint->Operate();
}
return pConstraint;
}
//-------------------------------------------------------------------------------------------------
// Create a Point constraint which will control the position of the specified dag such that it
// matches the weighted target position
//-------------------------------------------------------------------------------------------------
CDmeRigPointConstraintOperator* CDmAnimUtils::CreatePointConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
{
CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_POINT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
return CastElement< CDmeRigPointConstraintOperator >( pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Create a Orient constraint which will control the orientation of the specified dag such that it
// matches the weighted target orientation
//-------------------------------------------------------------------------------------------------
CDmeRigOrientConstraintOperator* CDmAnimUtils::CreateOrientConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
{
CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_ORIENT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
return CastElement< CDmeRigOrientConstraintOperator >( pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Create a Parent constraint which will control the position and orientation of the specified dag
// such the dag behaves as if it is a child of the transform defined by the weighted target list
//-------------------------------------------------------------------------------------------------
CDmeRigParentConstraintOperator* CDmAnimUtils::CreateParentConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight )
{
CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_PARENT, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, true );
return CastElement< CDmeRigParentConstraintOperator >( pConstraint );
}
//-------------------------------------------------------------------------------------------------
// Create an Aim constraint which will control the orientation of the specified dag such the it
// points toward the weighted target position
//-------------------------------------------------------------------------------------------------
CDmeRigAimConstraintOperator* CDmAnimUtils::CreateAimConstraint( char const *pchName, CDmeDag *pConstrainedDag, const CUtlVector< CDmeDagPtr > &targetDagList, bool bPreserveOffset, float flWeight, const Vector &upVector, TransformSpace_t upSpace, const CDmeDag* pReferenceDag )
{
CDmeRigAimConstraintOperator *pAimConstraint = NULL;
CDmeRigBaseConstraintOperator *pConstraint = CreateConstraint( pchName, CT_AIM, pConstrainedDag, targetDagList, bPreserveOffset, flWeight, false );
if ( pConstraint )
{
pAimConstraint = CastElement< CDmeRigAimConstraintOperator >( pConstraint );
if ( pAimConstraint )
{
// Translate all spaces into a dag node by which the space is defined
if ( upSpace != TS_REFERENCE_SPACE )
{
pReferenceDag = NULL;
const CDmeDag *pSlave = pAimConstraint->GetSlave();
if ( pSlave && ( upSpace == TS_OBJECT_SPACE ) )
{
pReferenceDag = pSlave->GetParent();
}
}
pAimConstraint->SetUpVector( upVector, bPreserveOffset, pReferenceDag );
pAimConstraint->Operate();
}
}
return pAimConstraint;
}
//-------------------------------------------------------------------------------------------------
// Create an ik constraint controlling the 2 bone chain from the specified root dag to the
// specified end dag.
//-------------------------------------------------------------------------------------------------
CDmeRigIKConstraintOperator* CDmAnimUtils::CreateIKConstraint( const char *pchName, CDmeDag *pStartNode, CDmeDag *pEndNode, CDmeDag *pTargetDag, bool bPreserveOffset, const Vector &poleVector, CDmeDag *pPoleVectorTarget )
{
// All of the input dag nodes must be specified
if ( pStartNode == NULL || pEndNode == NULL || pTargetDag == NULL )
return NULL;
// Find the middle node and make sure the chain is valid.
CDmeDag *pMiddleNode = pEndNode->GetParent();
if ( pMiddleNode == NULL )
return NULL;
if ( pMiddleNode->GetParent() != pStartNode )
return NULL;
// Make sure there are no existing ik constraints on the nodes
if ( FindConstraintOnDag( pStartNode, CT_IK ) != NULL )
return NULL;
if ( FindConstraintOnDag( pMiddleNode, CT_IK ) != NULL )
return NULL;
if ( FindConstraintOnDag( pEndNode, CT_IK ) != NULL )
return NULL;
// Create the ik constraint
CDmeRigIKConstraintOperator *pIKConstraint = CreateElement< CDmeRigIKConstraintOperator >( pchName, pStartNode->GetFileId() );
if ( pIKConstraint == NULL )
return NULL;
// Set the joints that are controlled by the constraint
pIKConstraint->SetJoints( pStartNode, pMiddleNode, pEndNode );
// Disconnect the channels from the attributes of slave transform that are written by the constraint.
pIKConstraint->DisconnectTransformChannels();
// Add the handles to the constraint or update their weights if they already exist.
pIKConstraint->AddHandles( 1, &pTargetDag, NULL, bPreserveOffset, NULL );
// Initialize the pole vector, if a pole vector target dag is specified
// it takes precedence over the pole vector direction
pIKConstraint->SetPoleVector( poleVector );
pIKConstraint->SetPoleVectorTarget( pPoleVectorTarget );
// Initialize and run the constraint
pIKConstraint->Setup( bPreserveOffset );
pIKConstraint->Operate();
return pIKConstraint;
}
//-------------------------------------------------------------------------------------------------
// Allocate a constraint of the specified type.
//-------------------------------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmAnimUtils::InstanceConstraint( char const *pchName, EConstraintType eType, const DmFileId_t &fileId )
{
CDmeRigBaseConstraintOperator *pOperator = NULL;
switch ( eType )
{
case CT_POINT:
{
pOperator = CreateElement< CDmeRigPointConstraintOperator >( pchName, fileId );
}
break;
case CT_ORIENT:
{
pOperator = CreateElement< CDmeRigOrientConstraintOperator >( pchName, fileId );
}
break;
case CT_AIM:
{
pOperator = CreateElement< CDmeRigAimConstraintOperator >( pchName, fileId );
}
break;
case CT_IK:
{
pOperator = CreateElement< CDmeRigIKConstraintOperator >( pchName, fileId );
}
break;
case CT_PARENT:
{
pOperator = CreateElement< CDmeRigParentConstraintOperator>( pchName, fileId );
}
break;
default:
return NULL;
}
return pOperator;
}
//-------------------------------------------------------------------------------------------------
// Purpose: Find the position or orientation channel for the specified dag node
//-------------------------------------------------------------------------------------------------
CDmeChannel *CDmAnimUtils::FindDagTransformChannel( CDmeDag* pDag, const char *pchAttributeName )
{
if ( pDag == NULL )
return NULL;
CDmeChannel *pChannel = FindChannelTargetingElement( pDag->GetTransform(), pchAttributeName );
// If no channel was attach directly to the dag, check for a
// channel attached to constraint that is driving the dag node.
if ( pChannel == NULL )
{
// 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;
// Find the channel targeting the constraint slave
pChannel = FindChannelTargetingElement( pSlave, pchAttributeName );
if ( pChannel != NULL )
break;
}
}
return pChannel;
}
//-------------------------------------------------------------------------------------------------
// Purpose: Find the constraint of the specified type controlling the specified dag node
//-------------------------------------------------------------------------------------------------
CDmeRigBaseConstraintOperator *CDmAnimUtils::FindConstraintOnDag( CDmeDag* pDag, EConstraintType constraintType )
{
if ( pDag == NULL )
return NULL;
// 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 )
{
if ( pConstraint->GetConstraintType() == constraintType )
{
return pConstraint;
}
}
}
return NULL;
}
//-------------------------------------------------------------------------------------------------
// Create the position and orientation channels for the specified dag node if they do not already
// exist.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::CreateTransformChannelsForDag( CDmeDag *pDag, CDmeChannelsClip *pChannelsClip, bool bPosition, bool bOrientation, CDmeChannel *&pPosChannel, CDmeChannel *&pRotChannel )
{
pPosChannel = NULL;
pRotChannel = NULL;
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
CDmeTransformControl *pTransformControl = NULL;
// Create the position channel if it does not exist
if ( bPosition )
{
pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
if ( pPosChannel )
{
pTransformControl = CastElement< CDmeTransformControl >( pPosChannel->GetFromElement() );
}
else
{
char name[ 256 ];
V_snprintf( name, sizeof( name ), "%s_p", pDag->GetName() );
pPosChannel = CreateElement< CDmeChannel >( name, pDag->GetFileId() );
pPosChannel->SetMode( CM_PLAY );
pPosChannel->CreateLog( AT_VECTOR3 );
pPosChannel->SetOutput( pTransform, "position" );
if ( pChannelsClip )
{
pChannelsClip->m_Channels.AddToTail( pPosChannel );
}
}
}
// Create the orientation channel if it does not exist
if ( bOrientation )
{
pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
if ( pRotChannel )
{
pTransformControl = CastElement< CDmeTransformControl >( pRotChannel->GetFromElement() );
}
else
{
char name[ 256 ];
V_snprintf( name, sizeof( name ), "%s_o", pDag->GetName() );
pRotChannel = CreateElement< CDmeChannel >( name, pDag->GetFileId() );
pRotChannel->SetMode( CM_PLAY );
pRotChannel->CreateLog( AT_QUATERNION );
pRotChannel->SetOutput( pTransform, "orientation" );
if ( pChannelsClip )
{
pChannelsClip->m_Channels.AddToTail( pRotChannel );
}
}
}
// If either channel is valid make sure a transform control is properly attached
if ( pPosChannel || pRotChannel )
{
if ( pTransformControl == NULL )
{
pTransformControl = CreateElement< CDmeTransformControl >( pDag->GetName(), pDag->GetFileId() );
}
if ( pPosChannel )
{
pTransformControl->SetPosition( pTransform->GetPosition() );
pTransformControl->SetPositionChannel( pPosChannel );
pPosChannel->SetInput( pTransformControl->GetPositionAttr() );
}
if ( pRotChannel )
{
pTransformControl->SetOrientation( pTransform->GetOrientation() );
pTransformControl->SetOrientationChannel( pRotChannel );
pRotChannel->SetInput( pTransformControl->GetOrientationAttr() );
}
}
}
//-------------------------------------------------------------------------------------------------
// Print the position and orientation of the dag over time for debugging purposes
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::PrintDagTransformOverTime( CDmeDag* pDag, CDmeClip *pShot, CDmeClip *pMovie )
{
// Find the channels which will be needed to evaluate
// the position of the child and the parent dag.
CUtlVector< CDmeOperator* > operatorList;
pDag->FindRelevantOperators( operatorList );
CUtlVector< DmeTime_t > keyTimes;
CompileKeyTimeList( operatorList, keyTimes, NULL, pShot, pMovie );
CUtlVector< matrix3x4_t > transformList;
GenerateDagWorldTransformList( pDag, keyTimes, transformList, pShot, pMovie );
int nNumTransforms = transformList.Count();
for ( int i = 0; i < nNumTransforms; ++i )
{
Vector pos;
QAngle rot;
MatrixAngles( transformList[ i ], rot, pos );
Msg( "%i : pos < %f, %f, %f >, rot< %f, %f, %f >\n", keyTimes[ i ].GetTenthsOfMS(), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z );
}
}
//-------------------------------------------------------------------------------------------------
// Update the logs of the dag so the the current local transform is the only value in the log
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::SetLogsToCurrentTransform( CDmeDag *pDag )
{
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
matrix3x4_t mTransform;
pTransform->GetTransform( mTransform );
Vector pos;
Quaternion rot;
MatrixAngles( mTransform, rot, pos );
CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
if ( pPosChannel )
{
CDmeTypedLog< Vector > *pPosLog = CastElement< CDmeTypedLog< Vector > >( pPosChannel->GetLog() );
if ( pPosLog )
{
pPosLog->ClearKeys();
pPosLog->SetKey( pPosChannel->GetCurrentTime(), pos );
}
}
CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
if ( pRotChannel )
{
CDmeTypedLog< Quaternion > *pRotLog = CastElement< CDmeTypedLog< Quaternion > >( pRotChannel->GetLog() );
if ( pRotLog )
{
pRotLog->ClearKeys();
pRotLog->SetKey( pRotChannel->GetCurrentTime(), rot );
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Generate log samples for the specified dag node, if a parent is provided, generate the
// samples in the space of that parent, otherwise generate the samples in world space.
//
// Input : pDag - Pointer to the dag node for which log samples are to be generated.
// Input : pParent - Pointer to the dag node whose space samples are to be generated in
// Input : bPosition - Flag indicating if the position log of the transform will be modified
// Input : bRotation - Flag indicating if the rotation log of the transform will be modified
// Input : pTimeSelection - Pointer to a time selection over which the samples are to be generated,
// if NULL samples will be generated at all key points for the log.
//
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GenerateLogSamples( CDmeDag* pDag, CDmeDag *pParent, bool bPosition, bool bRotation, const DmeLog_TimeSelection_t *pTimeSelection )
{
if ( pDag == NULL )
return;
CDmeClip *pShot = NULL;
CDmeClip *pMovie = NULL;
FindShotAndMovieForDag( pDag, pShot, pMovie );
// If position and rotation are both false then there is nothing to do.
if ( !bPosition && !bRotation )
return;
// If a time selection is provided use the re-sample interval and perform samples through the
// time selection, otherwise build a list of all of the key times for the relevant channels
CUtlVector< DmeTime_t > keyTimes;
// Find the channels which will be needed to evaluate
// the position of the child and the parent dag.
CUtlVector< CDmeOperator* > operatorList;
pDag->FindRelevantOperators( operatorList );
if ( pParent )
{
pParent->FindRelevantOperators( operatorList );
}
CompileKeyTimeList( operatorList, keyTimes, pTimeSelection, pShot, pMovie );
// Get the world space transform of the parent and the child at all of the key times
CUtlVector< matrix3x4_t > parentTransformList;
CUtlVector< matrix3x4_t > childTransformList;
GenerateDagWorldTransformList( pDag, keyTimes, childTransformList, pShot, pMovie );
if ( pParent )
{
GenerateDagWorldTransformList( pParent, keyTimes, parentTransformList, pShot, pMovie );
}
// Find the existing channels associated with the position and orientation of the dag transform
CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
// Find the channels clip containing the position and rotation channel. They should both be in the
// same channels clip, so we pick the channels clip from the position unless position is not used
// and the rotation channels clip is valid, or if the position channels clip is not valid.
CDmeChannelsClip *pChannelsClipPos = FindAncestorReferencingElement< CDmeChannelsClip >( pPosChannel );
CDmeChannelsClip *pChannelsClipRot = FindAncestorReferencingElement< CDmeChannelsClip >( pRotChannel );
CDmeChannelsClip *pChannelsClip = ( ( !bPosition && ( pChannelsClipRot != NULL ) ) || ( pChannelsClipPos == NULL ) ) ? pChannelsClipRot : pChannelsClipPos;
// Ensure that the dag has channels for position and rotation
CreateTransformChannelsForDag( pDag, pChannelsClip, bPosition, bRotation, pPosChannel, pRotChannel );
// Find the position and rotation channels for the child dag and create a new layer in each channel
CDmeLog *pPosLog = ( ( pPosChannel != NULL ) && bPosition ) ? pPosChannel->GetLog() : NULL;
CDmeLog *pRotLog = ( ( pRotChannel != NULL ) && bRotation ) ? pRotChannel->GetLog() : NULL;
CDmeTypedLogLayer< Vector > *pPosLayer = ( pPosLog != NULL ) ? CastElement< CDmeTypedLogLayer< Vector > >( pPosLog->AddNewLayer() ) : NULL;
CDmeTypedLogLayer< Quaternion > *pRotLayer = ( pRotLog != NULL ) ? CastElement< CDmeTypedLogLayer< Quaternion > >( pRotLog->AddNewLayer() ) : NULL;
Assert( pPosLayer || !bPosition );
Assert( pRotLayer || !bRotation );
// Build the clip stack to convert the global time into a local time
DmeClipStack_t clipStack;
if ( pChannelsClip )
{
pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
}
else if ( pMovie && pShot )
{
clipStack.AddClipToTail( pMovie );
clipStack.AddClipToTail( pShot );
}
// Disable the undo, the only thing that is actually
// needed is the add new layer and the flatten layers.
CDisableUndoScopeGuard undosg;
Vector position = vec3_origin;
Quaternion rotation = quat_identity;
DmeTime_t logTime = DMETIME_ZERO;
int nTimes = keyTimes.Count();
for ( int iTime = 0; iTime < nTimes; ++iTime )
{
// Calculate the time relative to the log
logTime = clipStack.ToChildMediaTime( keyTimes[ iTime ], false );
if ( pParent )
{
// Calculate the transform of the child relative to the parent
// needed to keep the child in its original location in world space.
const matrix3x4_t &parentMatrix = parentTransformList[ iTime ];
const matrix3x4_t &worldChildMatrix = childTransformList[ iTime ];
matrix3x4_t invParentMatrix;
matrix3x4_t localChildMatrix;
MatrixInvert( parentMatrix, invParentMatrix );
ConcatTransforms( invParentMatrix, worldChildMatrix, localChildMatrix );
MatrixAngles( localChildMatrix, rotation, position );
}
else
{
const matrix3x4_t &worldMatrix = childTransformList[ iTime ];
MatrixAngles( worldMatrix, rotation, position );
}
// Set a key storing the new position and rotation of the child at the current time
if ( pPosLayer )
{
pPosLayer->SetKey( logTime, position, SEGMENT_INTERPOLATE, CURVE_DEFAULT, false );
}
if ( pRotLayer )
{
pRotLayer->SetKey( logTime, rotation, SEGMENT_INTERPOLATE, CURVE_DEFAULT, false );
}
}
// If a time selection is specified blend the top level log layers containing the new
// position and orientation with the base log according to the time selection.
float flattenThreshold = DMELOG_DEFAULT_THRESHHOLD;
if ( pTimeSelection )
{
// Convert the time selection into the local time space of the log.
DmeLog_TimeSelection_t logTimeSelection = *pTimeSelection;
clipStack.ToChildMediaTime( logTimeSelection.m_nTimes );
flattenThreshold = pTimeSelection->m_flThreshold;
if ( pPosLog )
{
pPosLog->BlendLayersUsingTimeSelection( logTimeSelection );
}
if ( pRotLog )
{
pRotLog->BlendLayersUsingTimeSelection( logTimeSelection );
}
// Now flatten the all the layers in the log to finalize the new layer
{
// Re-enable the undo so the the flatten can be undone
CEnableUndoScopeGuard undosg;
if ( pPosLog )
{
pPosLog->FlattenLayers( flattenThreshold, 0 );
}
if ( pRotLog )
{
pRotLog->FlattenLayers( flattenThreshold, 0 );
}
}
}
else
{
CEnableUndoScopeGuard undosg;
// If not using a time selection then we only want data from the new log layer so just
// copy it to the base layer without blending and then remove all other layers.
if ( pPosLog )
{
CDmeLogLayer *pPosBaseLayer = pPosLog->GetLayer( 0 );
if ( pPosBaseLayer != pPosLayer )
{
pPosBaseLayer->CopyLayer( pPosLayer );
while ( pPosLog->GetNumLayers() > 1 )
{
pPosLog->RemoveLayerFromTail();
pPosLog->RemoveRedundantKeys( false );
}
}
}
if ( pRotLog )
{
CDmeLogLayer *pRotBaseLayer = pRotLog->GetLayer( 0 );
if ( pRotBaseLayer != pRotLayer )
{
pRotBaseLayer->CopyLayer( pRotLayer );
if ( pRotLog->GetNumLayers() > 1 )
{
pRotLog->RemoveLayerFromTail();
pRotLog->RemoveRedundantKeys( false );
}
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Generate a list of all of the world space transform of the specified dag node at each
// time in the provided list
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GenerateDagWorldTransformList( CDmeDag *pDag, const CUtlVector< DmeTime_t > &times, CUtlVector< matrix3x4_t > &transformList, CDmeClip *pShot, CDmeClip *pMovie )
{
// Make sure nothing here gets add to the undo queue.
CDisableUndoScopeGuard undosg;
// Find the channels and operators which must be updated in order to evaluate transform of the dag.
CUtlVector< CDmeChannel* > channelList;
CUtlVector< CDmeOperator* > operatorList;
pDag->FindRelevantOperators( channelList, operatorList );
// Build a clip stack for each channel in the list and save the current time of each of the channels.
CUtlVector< DmeClipStack_t > channelClipStackList;
CUtlVector< DmeTime_t > channelOriginalTimeList;
BuildClipStackList( channelList, channelClipStackList, channelOriginalTimeList, pMovie, pShot );
// Get the number of times to be sampled and resize the transform list to hold one matrix for each time
int nTimes = times.Count();
transformList.SetCount( nTimes );
for ( int iTime = 0; iTime < nTimes; ++iTime )
{
DmeTime_t time = times[ iTime ];
// Evaluate all of the channels relevant to the dag at the specified time
// so that the transform evaluations will correspond to the correct time.
PlayChannelsAtTime( time, channelList, operatorList, channelClipStackList );
// Get the world space transform of the target
matrix3x4_t &targetTransform = transformList[ iTime ];
pDag->GetAbsTransform( targetTransform );
}
// Restore all of the channels to the original times so that this operation does not have side effects.
PlayChannelsAtLocalTimes( channelOriginalTimeList, channelList, operatorList, false );
}
//-------------------------------------------------------------------------------------------------
// Update the position and orientation logs of the specified dag node so that the dag node's world
// space transform matches the provided list
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::SetDagWorldSpaceTransforms( CDmeDag* pDag, CUtlVector< DmeTime_t > &times, const CUtlVector< matrix3x4_t > &transformList, CDmeClip *pShot, CDmeClip *pMovie )
{
if ( pDag == NULL )
return;
CDmeTransform *pTransform = pDag->GetTransform();
if ( pTransform == NULL )
return;
// Make sure nothing here gets add to the undo queue.
CDisableUndoScopeGuard undosg;
// Find the existing channels associated with the position and orientation of the dag transform
CDmeChannel *pPosChannel = FindDagTransformChannel( pDag, TRANSFORM_POSITION );
CDmeChannel *pRotChannel = FindDagTransformChannel( pDag, TRANSFORM_ORIENTATION );
// Find the channels clip containing the position and rotation channel. They should both be in the
// same channels clip, so we pick the channels clip from the position unless position is not used
// and the rotation channels clip is valid, or if the position channels clip is not valid.
CDmeChannelsClip *pChannelsClipPos = FindAncestorReferencingElement< CDmeChannelsClip >( pPosChannel );
CDmeChannelsClip *pChannelsClipRot = FindAncestorReferencingElement< CDmeChannelsClip >( pRotChannel );
CDmeChannelsClip *pChannelsClip = ( pChannelsClipPos != NULL ) ? pChannelsClipPos : pChannelsClipRot;
// Build the clip stack to convert the global time into a local time
DmeClipStack_t clipStack;
if ( pChannelsClip )
{
pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
}
else if ( pMovie && pShot )
{
clipStack.AddClipToTail( pMovie );
clipStack.AddClipToTail( pShot );
}
// Find the channels and operators which must be updated in order to evaluate transform of the dag.
CUtlVector< CDmeChannel* > channelList;
CUtlVector< CDmeOperator* > operatorList;
pDag->FindRelevantOperators( channelList, operatorList );
// Build a clip stack for each channel in the list and save the current time of each of the channels.
CUtlVector< DmeClipStack_t > channelClipStackList;
CUtlVector< DmeTime_t > channelOriginalTimeList;
BuildClipStackList( channelList, channelClipStackList, channelOriginalTimeList, pMovie, pShot );
// Get the number of times to be sampled and resize the transform list to hold one matrix for each time
int nTimes = times.Count();
CUtlVector< DmeTime_t > localTimes;
CUtlVector< Vector > localPositions;
CUtlVector< Quaternion > localOrientations;
localTimes.SetCount( nTimes );
localPositions.SetCount( nTimes );
localOrientations.SetCount( nTimes );
for ( int iTime = 0; iTime < nTimes; ++iTime )
{
DmeTime_t time = times[ iTime ];
// Convert the global time to the local time of the logs
localTimes[ iTime ] = clipStack.ToChildMediaTime( time, false );
// Evaluate all of the channels relevant to the dag at the specified time
// so that the transform evaluations will correspond to the correct time.
PlayChannelsAtTime( time, channelList, operatorList, channelClipStackList );
// Get the world space transform of the target
const matrix3x4_t &targetTransform = transformList[ iTime ];
pDag->SetAbsTransform( targetTransform );
localPositions[ iTime ] = pTransform->GetPosition();
localOrientations[ iTime ] = pTransform->GetOrientation();
}
// Restore all of the channels to the original times so that this operation does not have side effects.
PlayChannelsAtLocalTimes( channelOriginalTimeList, channelList, operatorList, false );
// Write the positions to the log
if ( pPosChannel )
{
CDmeLog *pPosLog = pPosChannel->GetLog();
if ( pPosLog )
{
CDmeTypedLogLayer< Vector > *pPosLayer = CastElement< CDmeTypedLogLayer< Vector > >( pPosLog->GetLayer( 0 ) );
if ( pPosLayer )
{
CEnableUndoScopeGuard undosg;
pPosLayer->SetAllKeys( localTimes, localPositions );
pPosLog->RemoveRedundantKeys( false );
}
}
}
// Write the orientations to the log
if ( pRotChannel )
{
CDmeLog *pRotLog = pRotChannel->GetLog();
if ( pRotLog )
{
CDmeTypedLogLayer< Quaternion > *pRotLayer = CastElement< CDmeTypedLogLayer< Quaternion > >( pRotLog->GetLayer( 0 ) );
if ( pRotLayer )
{
CEnableUndoScopeGuard undosg;
pRotLayer->SetAllKeys( localTimes, localOrientations );
pRotLog->RemoveRedundantKeys( false );
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Create a list of all of the key times in the provided list of channels
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::CompileKeyTimeList( const CUtlVector< CDmeOperator* > &channelList, CUtlVector< DmeTime_t > &keyTimes, const DmeLog_TimeSelection_t *pTimeSelection, CDmeClip *pShot, CDmeClip *pMovie )
{
// Make sure nothing here gets add to the undo queue.
CDisableUndoScopeGuard undosg;
// Iterate through all of the provided channels adding
// the time of all of the keys in each channel to list.
CUtlRBTree< DmeTime_t > logTimes( 0, 0, DefLessFunc( DmeTime_t ) );
int nChannels = channelList.Count();
for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
{
CDmeChannel *pChannel = CastElement< CDmeChannel >( channelList[ iChannel ] );
if ( pChannel == NULL )
continue;
CDmeLog *pLog = pChannel->GetLog();
if ( pLog == NULL )
continue;
// Build the clip stack for the channel
CDmeChannelsClip *pChannelsClip = FindAncestorReferencingElement< CDmeChannelsClip >( pChannel );
if ( pChannelsClip == NULL )
continue;
DmeClipStack_t clipStack;
pChannelsClip->BuildClipStack( &clipStack, pMovie, pShot );
// Iterate through all of the keys in the log and
// make sure there is an entry for the time of the key.
int nKeys = pLog->GetKeyCount();
// If the log only has one key, we can ignore the time
// since the value will be the same throughout time.
if ( nKeys < 2 )
continue;
for ( int iKey = 0; iKey < nKeys; ++iKey )
{
// Get the time of the key and convert it to a global time
DmeTime_t keyTime = pLog->GetKeyTime( iKey );
if ( ( keyTime <= DMETIME_MINTIME ) || ( keyTime >= DMETIME_MAXTIME ) )
continue;
DmeTime_t time = clipStack.FromChildMediaTime( keyTime, false );
// If a time selection was specified, add only times within it.
if ( pTimeSelection )
{
if ( ( time < pTimeSelection->m_nTimes[ TS_LEFT_FALLOFF ] ) ||
( time > pTimeSelection->m_nTimes[ TS_RIGHT_FALLOFF ] ) )
continue;
}
// Add the key time if it is valid and has not already been added.
if ( ( time <= DMETIME_MINTIME ) || ( time >= DMETIME_MAXTIME ) )
continue;
logTimes.InsertIfNotFound( time );
}
}
DmeTime_t firstTime = DMETIME_ZERO;
DmeTime_t lastTime = DMETIME_ZERO;
if ( pTimeSelection )
{
firstTime = pTimeSelection->m_nTimes[ TS_LEFT_FALLOFF ];
lastTime = pTimeSelection->m_nTimes[ TS_RIGHT_FALLOFF ];
}
else if ( logTimes.Count() > 0 )
{
firstTime = logTimes[ logTimes.FirstInorder() ];
lastTime = logTimes[ logTimes.LastInorder() ];
}
// Use a re-sample time of 50ms unless otherwise specified by the time selection
DmeTime_t resampleInterval = DmeTime_t( 500 );
if ( pTimeSelection )
{
resampleInterval = MAX( pTimeSelection->m_nResampleInterval, DMETIME_MINDELTA );
}
// Calculate the starting time for the re-sampling. In order to try to keep the sampling
// consistent, make sure the starting time is on a multiple of the re-sample time.
int tmsResampleInterval = resampleInterval.GetTenthsOfMS();
int tmsFirstTime = firstTime.GetTenthsOfMS();
DmeTime_t sampleStartTime;
if ( tmsFirstTime < 0 )
{
int resampleStartBase = ( ( tmsFirstTime + 1 ) / tmsResampleInterval );
sampleStartTime = DmeTime_t( tmsResampleInterval * resampleStartBase );
}
else
{
int resampleStartBase = ( tmsFirstTime / tmsResampleInterval );
sampleStartTime = DmeTime_t( tmsResampleInterval * ( resampleStartBase + 1 ) );
}
Assert( sampleStartTime > firstTime );
Assert( sampleStartTime <= ( firstTime + resampleInterval ) );
// Calculate the approximate number of samples so that the key times array can be preallocated.
int nTimes = logTimes.Count() + ( lastTime - firstTime ).GetTenthsOfMS() / tmsResampleInterval;
keyTimes.RemoveAll();
keyTimes.EnsureCapacity( nTimes + 8 );
// Set the log time to either the first entry in the set of log times,
// or to the last time if there are no entries in the set of log times.
DmeTime_t logTime = lastTime;
int iLogTime = logTimes.FirstInorder();
if ( iLogTime != logTimes.InvalidIndex() )
{
logTime = logTimes[ iLogTime ];
}
// If the first log time is not the first time, with the first time, as there
// is nothing else that will since the sample times start after the first time.
if ( logTime != firstTime )
{
keyTimes.AddToTail( firstTime );
}
// Iterate through the sample times and log times, adding sample times until the next log
// time is reached and then adding log times until the next sample time is reached.
DmeTime_t sampleTime = sampleStartTime;
do
{
// Add sample time until the next log time is reached
while ( sampleTime < logTime )
{
keyTimes.AddToTail( sampleTime );
sampleTime += resampleInterval;
}
// If the next sample time is equal to the next log time, move the
// sample time up one more step since the log time will be added to
// the list. This guarantees sampleTime is greater than logTime.
if ( sampleTime == logTime )
{
sampleTime += resampleInterval;
}
// Add log times until the next sample time is reached or the last log time is
// hit. If the last log time is added, make the next log time the last time,
// this ensures the last time will be added even if it is not a sample time.
while ( logTime < sampleTime )
{
Assert( keyTimes.Find( logTime ) == keyTimes.InvalidIndex() );
keyTimes.AddToTail( logTime );
if ( iLogTime == logTimes.InvalidIndex() )
break;
iLogTime = logTimes.NextInorder( iLogTime );
if ( iLogTime == logTimes.InvalidIndex() )
{
if ( lastTime == logTime )
break;
logTime = lastTime;
}
else
{
logTime = logTimes[ iLogTime ];
}
}
}
while ( sampleTime <= lastTime );
Assert( logTime == lastTime );
Assert( keyTimes.Count() > 0 );
Assert( keyTimes[ keyTimes.Count() - 1 ] == lastTime );
for ( int i = 1; i < keyTimes.Count(); ++i )
{
Assert( keyTimes[ i - 1 ] != keyTimes[ i ] );
Assert( keyTimes[ i - 1 ] < keyTimes [ i ] );
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Find the position and orientation controls for the specified transform.
//-------------------------------------------------------------------------------------------------
CDmeTransformControl *CDmAnimUtils::GetTransformControl( const CDmeDag *pDag )
{
if ( pDag == NULL )
return NULL;
CUtlVector< CDmeChannel* > channelList( 0, 8 );
if ( pDag->GetTransform() )
{
FindAncestorsReferencingElement( pDag->GetTransform(), channelList );
}
CUtlVector< CDmeConstraintSlave* > slaveList( 0, 4 );
FindAncestorsReferencingElement( pDag, slaveList );
int nSlaves = slaveList.Count();
for ( int iSlave = 0; iSlave < nSlaves; ++iSlave )
{
CDmeConstraintSlave *pSlave = slaveList[ iSlave ];
if ( pSlave )
{
FindAncestorsReferencingElement( pSlave, channelList );
}
}
int nChannels = channelList.Count();
for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
{
CDmeChannel *pChannel = channelList[ iChannel ];
if ( pChannel == NULL )
continue;
CDmElement *pControl = pChannel->GetFromElement();
CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pControl );
if ( pTransformControl )
{
return pTransformControl;
}
}
return NULL;
}
//-------------------------------------------------------------------------------------------------
// Purpose: Get the local space default transform for the specified dag node
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GetDefaultTransform( CDmeDag *pDagNode, matrix3x4_t &defaultTransform )
{
CDmeTransformControl *pTransformControl = GetTransformControl( pDagNode );
Vector defaultPosition = vec3_origin;
Quaternion defaultOrientation = quat_identity;
if ( pTransformControl )
{
defaultPosition = pTransformControl->GetDefaultPosition();
defaultOrientation = pTransformControl->GetDefaultOrientation();
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Get the world space default transform for the specified dag node
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GetDefaultAbsTransform( CDmeDag *pDagNode, matrix3x4_t &absDefaultTransform )
{
matrix3x4_t parentToWorld;
matrix3x4_t localMatrix;
if ( pDagNode == NULL )
{
SetIdentityMatrix( absDefaultTransform );
return;
}
GetDefaultTransform( pDagNode, localMatrix );
GetDefaultAbsTransform( pDagNode->GetParent(), parentToWorld );
ConcatTransforms( parentToWorld, localMatrix, absDefaultTransform );
}
//-------------------------------------------------------------------------------------------------
// Purpose: Find and operate all of the channels driving the specified dag nodes
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::OperateDagChannels( const CUtlVector< CDmeDag* > &dagList, ChannelMode_t mode, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
{
// Get all of the channels driving the selected dag nodes
CUtlVector< CDmeChannel* > channelList;
GetChannelsForDags( dagList, channelList );
// Operate all of the channels in the currently selected mode in order
// to apply the values from the controls to the dag node transforms.
if ( mode == CM_RECORD )
{
RecordChannels( channelList, timeSelection, pShot, pMovie );
}
else
{
OperateChannels( channelList, timeSelection, pShot, pMovie );
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Get all of the channels directly driving the specified dag nodes
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::GetChannelsForDags( const CUtlVector< CDmeDag* > &dagList, CUtlVector< CDmeChannel* > &channelList )
{
int nDagNodes = dagList.Count();
channelList.EnsureCapacity( channelList.Count() + ( nDagNodes * 2 ) );
for ( int iNode = 0; iNode < nDagNodes; ++iNode )
{
const CDmeDag *pDagNode = dagList[ iNode ];
if ( pDagNode )
{
FindAncestorsReferencingElement( pDagNode->GetTransform(), channelList );
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Operate all of the provided channels in the specified mode.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::OperateChannels( const CUtlVector< CDmeChannel* > &channelList, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
{
int nChannels = channelList.Count();
for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
{
CDmeChannel *pChannel = channelList[ iChannel ];
if ( pChannel )
{
// Newly created channels may not have the current time, so
// make sure the time on the channel is correct before operating.
DmeClipStack_t clipStack;
pChannel->BuildClipStack( &clipStack, pMovie, pShot );
DmeTime_t localtime = clipStack.ToChildMediaTime( timeSelection.m_tHeadPosition );
pChannel->SetCurrentTime( localtime );
pChannel->Operate();
}
}
}
//-------------------------------------------------------------------------------------------------
// Purpose: Operate all of the provided channels in the specified mode.
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::RecordChannels( const CUtlVector< CDmeChannel* > &channelList, const DmeLog_TimeSelection_t &timeSelection, CDmeClip *pShot, CDmeClip *pMovie )
{
int nChannels = channelList.Count();
if ( nChannels == 0 )
return;
g_pChannelRecordingMgr->StartModificationLayer( &timeSelection, false );
g_pChannelRecordingMgr->StartLayerRecording( "Script channel record" );
for ( int iChannel = 0; iChannel < nChannels; ++iChannel )
{
CDmeChannel *pChannel = channelList[ iChannel ];
if ( pChannel )
{
// Newly created channels may not have the current time, so
// make sure the time on the channel is correct before operating.
DmeClipStack_t clipStack;
pChannel->BuildClipStack( &clipStack, pMovie, pShot );
DmeTime_t localtime = clipStack.ToChildMediaTime( timeSelection.m_tHeadPosition );
pChannel->SetCurrentTime( localtime );
TransformWriteMode_t transformWriteMode = g_pChannelRecordingMgr->GetTransformWriteMode();
g_pChannelRecordingMgr->SetTransformWriteMode( timeSelection.m_TransformWriteMode );
g_pChannelRecordingMgr->AddChannelToRecordingLayer( pChannel, LOG_COMPONENTS_ALL, pMovie, pShot );
g_pChannelRecordingMgr->SetTransformWriteMode( transformWriteMode );
pChannel->Operate();
}
}
g_pChannelRecordingMgr->FinishLayerRecording( timeSelection.m_flThreshold );
g_pChannelRecordingMgr->FinishModificationLayer();
}
//-------------------------------------------------------------------------------------------------
// Find the shot and the movie to which the specified dag node belongs
//-------------------------------------------------------------------------------------------------
void CDmAnimUtils::FindShotAndMovieForDag( const CDmeDag *pDag, CDmeClip *&pShot, CDmeClip *&pMovie )
{
pShot = NULL;
pMovie = NULL;
if ( pDag == NULL )
return;
// First find the root of the dag hierarchy, this should be the scene.
const CDmeDag *pCurrentDag = pDag;
const CDmeDag *pParent = pCurrentDag->GetParent();
while ( pParent )
{
pCurrentDag = pParent;
pParent = pCurrentDag->GetParent();
}
// Now find the film clips referencing the scene, take the first one that
// the scene attribute is the scene dag containing the dag node in question.
const CDmeDag *pRoot = pCurrentDag;
CUtlVector< CDmeFilmClip* > filmClipList( 0, 4 );
FindAncestorsReferencingElement( pRoot, filmClipList );
int nNumClips = filmClipList.Count();
for ( int iClip = 0; iClip < nNumClips; ++iClip )
{
CDmeFilmClip *pClip = filmClipList[ iClip ];
if ( pClip == NULL )
continue;
if ( pClip->GetScene() == pRoot )
{
pShot = pClip;
break;
}
// The active camera does not have to be in the scene
if ( pRoot == ( const CDmeDag* )( pClip->GetCamera() ) )
{
pShot = pClip;
break;
}
}
// If the shot was not found don't bother with the movie.
if ( pShot == NULL )
return;
// Find all of the tracks that reference the shot. There may be multiple if the shot has been imported into
// the active session from a session in the misc bin. We want the track that belongs to the active session.
CUtlVector< CDmeTrack* > trackList;
FindAncestorsReferencingElement( pShot, trackList );
int nNumTracks = trackList.Count();
for ( int iTrack = 0; iTrack < nNumTracks; ++iTrack )
{
CDmeTrack *pTrack = trackList[ iTrack ];
if ( pTrack == NULL )
continue;
// The session should be root file element, so we can
// find the session using the file id of the shot
CDmElement *pSession = g_pDataModel->GetElement( g_pDataModel->GetFileRoot( pTrack->GetFileId() ) );
if ( pSession )
{
CDmeFilmClip *pClip = pSession->GetValueElement< CDmeFilmClip >( "activeClip" );
if ( pClip )
{
if ( pClip->FindTrackForClip( pShot ) == pTrack )
{
pMovie = pClip;
break;
}
}
}
}
}