//====== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======= // // Purpose: Implementation of the CDmeRig class, a class which groups a set of // associated constraints and operators together, allowing operations to be // performed on the group of elements. Also contains the implementation of // CDmeRigAnimSetElements, a helper class used to store a list of elements which // are all associated with a single animation set. // //============================================================================= #include "movieobjects/dmerig.h" #include "movieobjects/dmeanimationset.h" #include "movieobjects/dmeoperator.h" #include "movieobjects/dmeclip.h" #include "movieobjects/dmetrackgroup.h" #include "movieobjects/dmetrack.h" #include "movieobjects/dmerigconstraintoperators.h" #include "movieobjects/dmetransformcontrol.h" #include "movieobjects/dmechannel.h" #include "datamodel/dmelementfactoryhelper.h" #include "tier1/fmtstr.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // Expose this the classes to the scene database IMPLEMENT_ELEMENT_FACTORY( DmeRigAnimSetElements, CDmeRigAnimSetElements ); IMPLEMENT_ELEMENT_FACTORY( DmeRig, CDmeRig ); //------------------------------------------------------------------------------------------------- // Purpose: Provide post construction processing. //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::OnConstruction() { m_AnimationSet.Init( this, "animationSet" ); m_ElementList.Init( this, "elementList" ); m_HiddenGroups.Init( this, "hiddenGroups" ); } //------------------------------------------------------------------------------------------------- // Purpose: Provide processing and cleanup before shutdown //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::OnDestruction() { } //------------------------------------------------------------------------------------------------- // Purpose: Set the animation set elements in the list are to be associated with, only allowed // when the element list is empty. //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::SetAnimationSet( CDmeAnimationSet* pAnimationSet ) { // The element list must be empty when the animation set is assigned. Assert( m_ElementList.Count() == 0 ); if ( m_ElementList.Count() == 0 ) { m_AnimationSet = pAnimationSet; } } //------------------------------------------------------------------------------------------------- // Purpose: Add an element to the list //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::AddElement( CDmElement *pElement ) { if ( pElement == NULL ) return; m_ElementList.AddToTail( pElement ); } //------------------------------------------------------------------------------------------------- // Purpose: Remove the specified element from the list. Returns true if the element is found and // removed, return false if the element could not be found. //------------------------------------------------------------------------------------------------- bool CDmeRigAnimSetElements::RemoveElement( CDmElement *pElement ) { int index = m_ElementList.Find( pElement ); if ( index != m_ElementList.InvalidIndex() ) { m_ElementList.Remove( index ); return true; } return false; } //------------------------------------------------------------------------------------------------- // Purpose: Remove all of the elements from the list //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::RemoveAll() { m_ElementList.RemoveAll(); } //------------------------------------------------------------------------------------------------- // Purpose: Add all of the elements to the provided array //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::GetElements( CUtlVector< CDmElement* > &elementList ) const { int nElements = m_ElementList.Count(); for ( int iElement = 0; iElement < nElements; ++iElement ) { CDmElement *pElement = m_ElementList[ iElement ]; if ( pElement ) { elementList.AddToTail( pElement ); } } } //------------------------------------------------------------------------------------------------- // Add a control group to the list of hidden control groups //------------------------------------------------------------------------------------------------- void CDmeRigAnimSetElements::AddHiddenControlGroup( CDmeControlGroup *pControlGroup ) { m_HiddenGroups.AddToTail( pControlGroup->GetName() ); } //------------------------------------------------------------------------------------------------- // Purpose: Provide post construction processing. //------------------------------------------------------------------------------------------------- void CDmeRig::OnConstruction() { m_AnimSetList.Init( this, "animSetList" ); } //------------------------------------------------------------------------------------------------- // Purpose: Provide processing and cleanup before shutdown //------------------------------------------------------------------------------------------------- void CDmeRig::OnDestruction() { } //------------------------------------------------------------------------------------------------- // Purpose: Add an element to the rig //------------------------------------------------------------------------------------------------- void CDmeRig::AddElement( CDmElement* pElement, CDmeAnimationSet *pAnimationSet ) { if ( ( pElement == NULL ) || ( pAnimationSet == NULL ) ) return; // Search for an element set with the specified // animation set, if none is found, create one. CDmeRigAnimSetElements *pAnimSetElementList = FindOrCreateAnimSetElementList( pAnimationSet ); if ( pAnimSetElementList ) { pAnimSetElementList->AddElement( pElement ); } } //------------------------------------------------------------------------------------------------- // Set the state of the specified control group and add it to list of control group modified by // the rig //------------------------------------------------------------------------------------------------- void CDmeRig::HideControlGroup( CDmeControlGroup *pGroup ) { if ( pGroup == NULL ) return; CDmeAnimationSet *pAnimationSet = pGroup->FindAnimationSet( true ); if ( pAnimationSet == NULL ) return; CDmeRigAnimSetElements *pAnimSetElementList = FindOrCreateAnimSetElementList( pAnimationSet ); if ( pAnimSetElementList) { pGroup->SetVisible( false ); pAnimSetElementList->AddHiddenControlGroup( pGroup ); } } //------------------------------------------------------------------------------------------------- // Purpose: Remove an element from the rig //------------------------------------------------------------------------------------------------- void CDmeRig::RemoveElement( CDmElement *pElement, CDmeAnimationSet *pAnimationSet ) { if ( pElement == NULL ) return; // Search each of the animation set element lists for the specified element, if the element // is found and removed from an animation set element list, stop and don't search the others, // as each element should belong to only on animation set. int nAnimSets = m_AnimSetList.Count(); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements ) { if ( pAnimSetElements->AnimationSet() == pAnimationSet ) { if ( pAnimSetElements->RemoveElement( pElement ) ) break; } } } } //------------------------------------------------------------------------------------------------- // Purpose: Remove an animation set and all associated elements from the group //------------------------------------------------------------------------------------------------- void CDmeRig::RemoveAnimationSet( CDmeAnimationSet *pAnimationSet ) { int index = FindAnimSetElementList( pAnimationSet ); if ( index != m_AnimSetList.InvalidIndex() ) { m_AnimSetList.Remove( index ); } } //------------------------------------------------------------------------------------------------- // Determine if the rig has any animation sets associated with it //------------------------------------------------------------------------------------------------- bool CDmeRig::HasAnyAnimationSets() const { int nAnimSets = m_AnimSetList.Count(); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { if ( CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ] ) { if ( CDmeAnimationSet *pAnimSet = pAnimSetElements->AnimationSet() ) return true; } } return false; } //------------------------------------------------------------------------------------------------- // Purpose: Get the list of animation sets in the group //------------------------------------------------------------------------------------------------- void CDmeRig::GetAnimationSets( CUtlVector< CDmeAnimationSet* > &animationSetList ) const { int nAnimSets = m_AnimSetList.Count(); animationSetList.EnsureCapacity( animationSetList.Count() + nAnimSets ); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements != NULL ) { CDmeAnimationSet *pAnimSet = pAnimSetElements->AnimationSet(); if ( pAnimSet != NULL ) { animationSetList.AddToTail( pAnimSet ); } } } } //------------------------------------------------------------------------------------------------- // Purpose: Get the list of elements for the specified animation set //------------------------------------------------------------------------------------------------- void CDmeRig::GetAnimationSetElements( const CDmeAnimationSet* pAnimationSet, CUtlVector< CDmElement* > &elementList ) const { int nAnimSets = m_AnimSetList.Count(); // Count the number of total elements in all the animation sets int nTotalElements = 0; for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements != NULL ) { nTotalElements = pAnimSetElements->NumElements(); } } // Allocate enough space in the element list for all of the elements in the rig. elementList.EnsureCapacity( elementList.Count() + nTotalElements ); // Add all the elements in the rig to the provided element list for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements != NULL ) { pAnimSetElements->GetElements( elementList ); } } } //------------------------------------------------------------------------------------------------- // Purpose: Determine if the rig has any elements from the specified animation set //------------------------------------------------------------------------------------------------- bool CDmeRig::HasAnimationSet( const CDmeAnimationSet *pAnimationSet ) const { return ( FindAnimSetElementList( pAnimationSet ) != m_AnimSetList.InvalidIndex() ); } //------------------------------------------------------------------------------------------------- // Purpose: Find the element list for the specified animation set //------------------------------------------------------------------------------------------------- int CDmeRig::FindAnimSetElementList( const CDmeAnimationSet *pAnimationSet ) const { int nAnimSets = m_AnimSetList.Count(); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements == NULL ) continue; if ( pAnimSetElements->AnimationSet() == pAnimationSet ) { return iAnimSet; } } return m_AnimSetList.InvalidIndex(); } //------------------------------------------------------------------------------------------------- // Find the element list for the specified animation set or create one //------------------------------------------------------------------------------------------------- CDmeRigAnimSetElements *CDmeRig::FindOrCreateAnimSetElementList( CDmeAnimationSet *pAnimationSet ) { int nIndex = FindAnimSetElementList( pAnimationSet ); if ( nIndex != m_AnimSetList.InvalidIndex() ) return m_AnimSetList[ nIndex ]; CDmeRigAnimSetElements *pAnimSetElementList = CreateElement< CDmeRigAnimSetElements >( CFmtStr( "rigElements_%s", pAnimationSet->GetName() ), GetFileId() ); if ( pAnimSetElementList ) { pAnimSetElementList->SetAnimationSet( pAnimationSet ); m_AnimSetList.AddToTail( pAnimSetElementList ); } return pAnimSetElementList; } //------------------------------------------------------------------------------------------------- // Purpose: Build a list of all of the dag nodes which are influenced by rig, does not include // dag nodes that are part of the rig. //------------------------------------------------------------------------------------------------- void CDmeRig::FindInfluencedDags( CUtlVector< CDmeDag* > &dagList ) const { // Count the total number of elements, this is done to calculate a good pre-allocation size for lists int totalElements = 0; int nAnimSets = m_AnimSetList.Count(); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { totalElements += m_AnimSetList[ iAnimSet ]->NumElements(); } // First build a tree of all of the dag nodes that are part of the rig, we do this so that we // can quickly determine if a dag which is influenced by some element of the rig is part of the // rig and therefore should not be added to the list of influenced dag nodes. CUtlVector< CDmElement* > elementList( 0, totalElements ); CUtlRBTree< const CDmeDag* > rigDags( 0, totalElements, DefLessFunc( const CDmeDag *) ); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { const CDmaElementArray< CDmElement > &animSetElements = m_AnimSetList[ iAnimSet ]->Elements(); int nElements = animSetElements.Count(); for ( int iElement = 0; iElement < nElements; ++iElement ) { CDmElement *pElement = animSetElements[ iElement ]; if ( pElement ) { elementList.AddToTail( pElement ); CDmeDag *pDag = CastElement< CDmeDag >( pElement ); if ( pDag ) { rigDags.Insert( pDag ); } } } } // Now iterate through all of the elements that belong to the rig and find any dag nodes which // are influenced by the rig elements. This is done by looking for any operators and then // getting the output attributes of the operator and determining if any of those attributes // belong to a dag node which is not part of the rig. CUtlRBTree< CDmeDag* > influencedDags( 0, totalElements, DefLessFunc( CDmeDag *) ); CUtlVector< CDmAttribute* > outputAttributes( 0, 32 ); int nElements = elementList.Count(); for ( int iElement = 0; iElement < nElements; ++iElement ) { CDmElement *pElement = elementList[ iElement ]; CDmeOperator *pOperator = CastElement< CDmeOperator >( pElement ); if ( pOperator ) { outputAttributes.RemoveAll(); pOperator->GetOutputAttributes( outputAttributes ); int nAttributes = outputAttributes.Count(); for ( int iAttr = 0; iAttr < nAttributes; ++iAttr ) { CDmAttribute *pAttr = outputAttributes[ iAttr ]; if ( pAttr == NULL ) continue; CDmeDag *pDag = CastElement< CDmeDag >( pAttr->GetOwner() ); if ( pDag == NULL ) { CDmeTransform *pTransform = CastElement< CDmeTransform >( pAttr->GetOwner() ); if ( pTransform ) { pDag = pTransform->GetDag(); } } if ( pDag == NULL ) continue; // Make sure the dag is not part of the rig, if // not add it to the list of influenced dag nodes. if ( rigDags.Find( pDag ) == rigDags.InvalidIndex() ) { influencedDags.InsertIfNotFound( pDag ); } } } } // Copy the influenced dag nodes into the provided list int nDagNodes = influencedDags.Count(); dagList.SetCount( nDagNodes ); for ( int iDag = 0; iDag < nDagNodes; ++iDag ) { dagList[ iDag ] = influencedDags[ iDag ]; } } //------------------------------------------------------------------------------------------------- // Purpose: Remove all of elements in the rig from the specified shot //------------------------------------------------------------------------------------------------- void CDmeRig::RemoveElementsFromShot( CDmeFilmClip *pShot ) { // Find the animation set channels track group, this will be used to find // the the channels clip for each of the animation sets referenced by the rig. CDmeTrack *pAnimSetEditorTrack = NULL; CDmeTrackGroup *pTrackGroup = pShot->FindOrAddTrackGroup( "channelTrackGroup" ); if ( pTrackGroup ) { pAnimSetEditorTrack = pTrackGroup->FindOrAddTrack( "animSetEditorChannels", DMECLIP_CHANNEL ); } int nAnimSets = m_AnimSetList.Count(); for ( int iAnimSet = 0; iAnimSet < nAnimSets; ++iAnimSet ) { CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ iAnimSet ]; if ( pAnimSetElements == NULL ) continue; CDmeAnimationSet *pAnimSet = pAnimSetElements->AnimationSet(); if ( pAnimSet == NULL ) continue; CDmeChannelsClip *pChannelsClip = NULL; if ( pAnimSetEditorTrack ) { pChannelsClip = CastElement< CDmeChannelsClip >( pAnimSetEditorTrack->FindNamedClip( pAnimSet->GetName() ) ); } // Make a copy of the array since we may remove elements from the original while iterating const CDmaElementArray< CDmElement > &elements = pAnimSetElements->Elements(); int nElements = elements.Count(); CUtlVector< DmElementHandle_t > elementHandles( 0, nElements ); for ( int iElement = 0; iElement < nElements; ++iElement ) { CDmElement *pElement = elements[ iElement ]; if ( pElement ) { elementHandles.AddToTail( pElement->GetHandle() ); } } int nElementHandles = elementHandles.Count(); for ( int iHandle = 0; iHandle < nElementHandles; ++iHandle ) { // Get the element using its handle because it may have been destroyed already CDmElement *pElement = GetElement< CDmElement >( elementHandles[ iHandle ] ); if ( pElement == NULL ) continue; // If the element is an operator make sure it is removed from the // list of operators maintained by the shot and animation set. CDmeOperator *pOperator = CastElement< CDmeOperator >( pElement ); if ( pOperator ) { pAnimSet->RemoveOperator( pOperator ); pShot->RemoveOperator( pOperator ); // If the element is a channel remove it from the animation set's channel clip. if ( pChannelsClip ) { CDmeChannel *pChannel = CastElement< CDmeChannel >( pElement ); if ( pChannel ) { pChannelsClip->RemoveChannel( pChannel ); } } // If the element is a constraint reconnect the original // channels of the constrained dag back to the transform. CDmeRigBaseConstraintOperator *pConstraint = CastElement< CDmeRigBaseConstraintOperator >( pElement ); if ( pConstraint ) { pConstraint->ReconnectTransformChannels(); } } else if ( pElement->IsA( CDmeDag::GetStaticTypeSymbol() ) ) { CDmeDag *pDag = CastElement< CDmeDag >( pElement ); CDmeDag *pParent = pDag->GetParent(); if ( pParent ) { // Make sure the parent hasn't already been destroyed if ( g_pDataModel->GetElement( pParent->GetHandle() ) ) { pParent->RemoveChild( pDag ); } } // If the dag has any constraints on it, remove them CDmeRigBaseConstraintOperator::RemoveConstraintsFromDag( pDag ); } else if ( ( pElement->GetType() == CDmElement::GetStaticTypeSymbol() ) || pElement->IsA( CDmeTransformControl::GetStaticTypeSymbol() ) ) { // If the element is just only element assume it may be a control or if the element is // a transform control try to remove it from the animation set's list of controls. pAnimSet->RemoveControl( pElement ); } // Destroy the element, this is done because many elements refer to each other so if undo is enabled // the auto cleanup will not take place and the elements will persist until the undo history is cleared. g_pDataModel->DestroyElement( pElement->GetHandle() ); } // For iElement // Reset the visibility on control groups that were hidden by the rig SetHiddenControlGroupVisibility( pAnimSetElements, true ); // Clear the element list since they have all been destroyed pAnimSetElements->RemoveAll(); } // For iAnimSet m_AnimSetList.RemoveAll(); } //------------------------------------------------------------------------------------------------- // Set the visibility of the control groups in the hidden list //------------------------------------------------------------------------------------------------- void CDmeRig::SetHiddenControlGroupVisibility( CDmeRigAnimSetElements *pAnimSetElements, bool bVisible ) { CDmeAnimationSet *pAnimSet = pAnimSetElements->AnimationSet(); if ( pAnimSet == NULL ) return; const CDmaStringArray &hiddenGroupList = pAnimSetElements->HiddenControlGroups(); int nNumGroups = hiddenGroupList.Count(); for ( int iGroup = 0; iGroup < nNumGroups; ++iGroup ) { CDmeControlGroup *pGroup = pAnimSet->FindControlGroup( hiddenGroupList[ iGroup ] ); if ( pGroup ) { pGroup->SetVisible( bVisible ); } } } //------------------------------------------------------------------------------------------------- // Hide all of the control groups in the rig's list of hidden control groups //------------------------------------------------------------------------------------------------- void CDmeRig::HideHiddenControlGroups( CDmeAnimationSet *pAnimationSet ) { int nAnimSetIndex = FindAnimSetElementList( pAnimationSet ); if ( nAnimSetIndex == m_AnimSetList.InvalidIndex() ) return; CDmeRigAnimSetElements *pAnimSetElements = m_AnimSetList[ nAnimSetIndex ]; if ( pAnimSetElements == NULL ) return; SetHiddenControlGroupVisibility( pAnimSetElements, false ); } //------------------------------------------------------------------------------------------------- // Purpose: Remove the specified element from any rig which it may be associated with. //------------------------------------------------------------------------------------------------- void CDmeRig::RemoveElementFromRig( CDmElement *pElement ) { if ( pElement == NULL ) return; CUtlVector< CDmeRigAnimSetElements* > rigElementLists; FindAncestorsReferencingElement( pElement, rigElementLists ); for ( int iList = 0; iList < rigElementLists.Count(); ++iList ) { CDmeRigAnimSetElements *pElementList = rigElementLists[ iList ]; if ( pElementList ) { pElementList->RemoveElement( pElement ); } } } //------------------------------------------------------------------------------------------------- // Find all of the rigs which refer to the specified animation set //------------------------------------------------------------------------------------------------- void CollectRigsOnAnimationSet( CDmeAnimationSet *pAnimSet, CUtlVector< CDmeRig* > &rigList ) { CDmeFilmClip *pFilmClip = FindReferringElement< CDmeFilmClip >( pAnimSet, "animationSets" ); if ( pFilmClip == NULL ) return; CDmeDag *pScene = pFilmClip->GetScene(); if ( !pScene || !pAnimSet ) return; pScene->FindChildrenOfType( rigList ); int i = rigList.Count(); while ( --i >= 0 ) { CDmeRig *pRig = rigList[ i ]; if ( !pRig || !pRig->HasAnimationSet( pAnimSet ) ) { rigList.Remove( i ); } } }