//====== Copyright © 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 > ×, 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 > ×, 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; } } } } }