//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #include "movieobjects/dmechannel.h" #include "movieobjects/dmelog.h" #include "movieobjects/dmeclip.h" #include "movieobjects/dmetimeselection.h" #include "movieobjects/dmetransformcontrol.h" #include "datamodel/dmelementfactoryhelper.h" #include "datamodel/dmehandle.h" #include "datamodel/dmattribute.h" #include "tier0/vprof.h" #include "tier1/KeyValues.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // Paste data utility function declarations KeyValues *FindLayerInPasteData( const CUtlVector< KeyValues * > &list, CDmeLog *log ); static int FindSpanningLayerAndSetIntensity( DmeLog_TimeSelection_t &ts, LayerSelectionData_t *data ); void CopyPasteData( CUtlVector< KeyValues * > &dstList, const CUtlVector< KeyValues * > &srcList ); void DestroyPasteData( CUtlVector< KeyValues * > &list ); //----------------------------------------------------------------------------- // // CRecordingLayer // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Default constructor //----------------------------------------------------------------------------- CRecordingLayer::CRecordingLayer() : m_pPresetValuesDict( 0 ) , m_tHeadShotTime( DMETIME_INVALID ) , m_ProceduralType( 0 ) , m_OperationFlags( 0 ) , m_RandomSeed( 0 ) , m_flThreshold( 0 ) , m_flIntensity( 0 ) , m_RecordingMode( RECORD_PRESET ) , m_pUndoOperation( NULL ) , m_pfnAddChannelCallback( NULL ) , m_pfnFinishChannelCallback( NULL ) { } //----------------------------------------------------------------------------- // Destructor, releases any clipboard data or log layer data that may be held // by the recording layer. //----------------------------------------------------------------------------- CRecordingLayer::~CRecordingLayer() { DestroyPasteData( m_ClipboardData ); int nChannels = m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { LayerChannelInfo_t &info = m_LayerChannels[ iChannel ]; g_pDataModel->DestroyElement( info.m_hRawDataLayer ); } } //----------------------------------------------------------------------------- // Purpose: Determine if the specified channel is in the list of modify // channels, regardless of component flags. //----------------------------------------------------------------------------- bool ModifyChannel::IsChannelInList( const CUtlVector< ModifyChannel > &modifyList, CDmeChannel *pChannel ) { int nChannels = modifyList.Count(); for ( int i = 0; i < nChannels; ++i ) { if ( modifyList[ i ].m_pChannel == pChannel ) { return true; } } return false; } //----------------------------------------------------------------------------- // // CDmeChannelModificationLayer // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Default constructor //----------------------------------------------------------------------------- CDmeChannelModificationLayer::CDmeChannelModificationLayer() : m_bVisible( true ) { } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- CDmeChannelModificationLayer::~CDmeChannelModificationLayer() { } //----------------------------------------------------------------------------- // Purpose: Add a channel to the modification layer if it is not already // present, adding the channel to the modification layer causes a new log layer // to be added to the channel to which modifications will be made. // Input : pChannel - Pointer to the channel to be added. // enableUndo - Flag indicating if undo should be enabled when adding // the modification layer to the log of the channel. // Output : Returns the index of the location in the modification layer's array // of channels where the information for the channel was stored. //----------------------------------------------------------------------------- int CDmeChannelModificationLayer::AddChannel( CDmeChannel *pChannel, bool enableUndo ) { // Make sure the channel is valid and that is has a valid log. Assert( pChannel ); if ( pChannel == NULL ) return -1; CDmeLog* pLog = pChannel->GetLog(); if ( pLog == NULL ) return -1; // Check to see if the channel is already in the modification layer, // if so just update the reference count of the channel and return. int nChannels = m_ActiveChannels.Count(); int availableSlot = -1; for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { if ( m_ActiveChannels[ iChannel ].m_Channel == pChannel->GetHandle() ) { ++m_ActiveChannels[ iChannel ].m_RefCount; return iChannel; } if ( ( availableSlot < 0 ) && ( m_ActiveChannels[ iChannel ].m_Channel.Get() == NULL ) ) { Assert( m_ActiveChannels[ iChannel ].m_RefCount == 0 ); availableSlot = iChannel; } } // Add the new channel to the modification layer's list in the // available slot or at the end if there is not an available slot. if ( availableSlot < 0 ) { availableSlot = m_ActiveChannels.AddToTail(); } m_ActiveChannels[ availableSlot ].m_Channel = pChannel->GetHandle(); m_ActiveChannels[ availableSlot ].m_RefCount = 1; // Store the index of the current topmost layer, this is the layer which will be copied. int sourceLayerIndex = pLog->GetTopmostLayer(); // If there is only one layer in the log add a new log layer, this is the layer upon which modifications // within the modification layer will be applied to until the modification layer is completed. if ( sourceLayerIndex == 0 ) { pLog->GetLayer( sourceLayerIndex ); g_pDataModel->SetUndoEnabled( enableUndo ); CDmeLogLayer *pLayer = pLog->AddNewLayer(); g_pDataModel->SetUndoEnabled( false ); if ( ( pLayer ) && ( sourceLayerIndex >= 0 ) ) { CDmeLogLayer *pSrcLayer = pLog->GetLayer( sourceLayerIndex ); if ( pSrcLayer ) { pLayer->CopyLayer( pSrcLayer ); } // The modification layer is always considered infinite so // as long as it is active it will be used at any time. pLayer->SetInfinite( true, true ); } } // Return the index of the location in the array where the channel was added. return availableSlot; } //----------------------------------------------------------------------------- // Purpose: Flatten the log layers of all of the active channels onto the base // layer. // Input : bSaveChanges - Flag indicating if the changes recorded on the // modification layer should be applied to the base layer ( true ) or // completely discarded ( false ). // Input : bFlattenLayers - Flag indicating if the modification layers of the // logs should be flattened onto the base layer of the logs. Ignored // if bSaveChanges is false. //----------------------------------------------------------------------------- void CDmeChannelModificationLayer::Finish( bool bSaveChanges, bool bFlattenLayers, bool bRunChannelCallbacks ) { int nChannels = m_ActiveChannels.Count(); if ( ( bSaveChanges == false ) && ( bFlattenLayers == true ) ) { // If not saving the changes just remove all by the layers from the log except the base layer. for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { CDmeChannel *pChannel = m_ActiveChannels[ iChannel ].m_Channel.Get(); if ( pChannel ) { CDmeLog *pLog = pChannel->GetLog(); if ( pLog ) { while ( pLog->GetNumLayers() > 1 ) { pLog->RemoveLayerFromTail(); } } } } } else if ( bFlattenLayers ) { // If requested flatten the logs of the active channels. This flattens the logs completely, // so if there are any layers above the modification layer they will be flattened too. for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { CDmeChannel *pChannel = m_ActiveChannels[ iChannel ].m_Channel.Get(); if ( pChannel ) { CDmeLog *pLog = pChannel->GetLog(); if ( pLog ) { pLog->FlattenLayers( 0.0f, 0 ); } } } } if ( bRunChannelCallbacks ) { int nLayers = m_RecordingLayerStack.Count(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { CDmeChannelRecordingMgr::RunFinishCallbacksOnRecordingLayer( &m_RecordingLayerStack[ iLayer ] ); } } // Remove all recording layers m_RecordingLayerStack.Purge(); // Remove all channels m_ActiveChannels.Purge(); } //----------------------------------------------------------------------------- // Purpose: Add a recording layer to the modification layer. // Output : Returns a pointer to the recording layer which was added. //----------------------------------------------------------------------------- CRecordingLayer* CDmeChannelModificationLayer::AddRecordingLayer() { int index = m_RecordingLayerStack.AddToTail(); return &m_RecordingLayerStack[ index ]; } //----------------------------------------------------------------------------- // Purpose: Remove the last recording layer from the modification layer. //----------------------------------------------------------------------------- void CDmeChannelModificationLayer::RemoveLastRecordingLayer() { // Iterate through the channels in the recording layer and update the reference count of the // channel in the modification layer. If the channel is no longer referenced by any of the // recording layers in the modification layer it will be removed from the modification layer. CRecordingLayer &recLayer = m_RecordingLayerStack.Tail(); int nChannels = recLayer.m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { int index = recLayer.m_LayerChannels[ iChannel ].m_ModLayerIndex; Assert( index >= 0 ); Assert( index < m_ActiveChannels.Count() ); if ( index >= 0 && index < m_ActiveChannels.Count() ) { --m_ActiveChannels[ index ].m_RefCount; if ( m_ActiveChannels[ index ].m_RefCount < 1 ) { Assert( m_ActiveChannels[ index ].m_RefCount == 0 ); m_ActiveChannels[ index ].m_Channel = NULL; } } } m_RecordingLayerStack.RemoveMultipleFromTail( 1 ); } //----------------------------------------------------------------------------- // Purpose: Restore the modification log layer of each of the channels in the // modification layer to their original state before any recording layers were // applied by copying the base layer. //----------------------------------------------------------------------------- void CDmeChannelModificationLayer::WipeChannelModifications() { int nChannels = m_ActiveChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { CDmeChannel *pChannel = m_ActiveChannels[ iChannel ].m_Channel.Get(); if ( pChannel ) { // Get the log from the channel CDmeLog *pLog = pChannel->GetLog(); if ( pLog == NULL ) continue; // Get the number of layers, it must be at least two, // the base layer and the modification layer. int numLayers = pLog->GetNumLayers(); if ( numLayers < 2 ) { Assert( numLayers >= 2 ); continue; } // Get the modification layer and the base layer and then copy the base layer into the // modification layer, overwriting anything that is currently in the modification layer. CDmeLogLayer *pModLayer = pLog->GetLayer( numLayers - 1 ); CDmeLogLayer *pBaseLayer = pLog->GetLayer( numLayers - 2 ); pModLayer->CopyLayer( pBaseLayer ); // Modification layer is always infinite, while // it is active no layers beneath it matter. pModLayer->SetInfinite( true, true ); } } } //----------------------------------------------------------------------------- // Purpose: Get the number of recording layers in the modification layer //----------------------------------------------------------------------------- int CDmeChannelModificationLayer::NumRecordingLayers() const { return m_RecordingLayerStack.Count(); } //----------------------------------------------------------------------------- // Purpose: Get a reference to the specified recording layer //----------------------------------------------------------------------------- CRecordingLayer &CDmeChannelModificationLayer::GetRecordingLayer( int index ) { return m_RecordingLayerStack[ index ]; } //----------------------------------------------------------------------------- // Purpose: Return if the modification layer visible to the user //----------------------------------------------------------------------------- bool CDmeChannelModificationLayer::IsVisible() const { return m_bVisible; } //----------------------------------------------------------------------------- // Apply the specified transform write mode all of the active channels in each // of the recording layers //----------------------------------------------------------------------------- void CDmeChannelModificationLayer::UpdateTransformWriteMode( TransformWriteMode_t mode ) { int nLayers = m_RecordingLayerStack.Count(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { CRecordingLayer &layer = m_RecordingLayerStack[ iLayer ]; int nChannels = layer.m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { LayerChannelInfo_t &channelInfo = layer.m_LayerChannels[ iChannel ]; channelInfo.m_TransformWriteMode = mode; } } } //----------------------------------------------------------------------------- // CUndoAddRecoringLayer // // Undo operation for adding a recording layer to the modification layer, // removes the last recording layer from the modification layer. // //----------------------------------------------------------------------------- class CUndoAddRecordingLayer : public CUndoElement { public: CUndoAddRecordingLayer( const char *pUndoDesc, CDmeChannelRecordingMgr* pRecordingMgr ) : CUndoElement( pUndoDesc ) , m_pRecordingMgr( pRecordingMgr ) , m_pPresetValuesDict( NULL ) , m_tHeadShotTime( NULL ) , m_ProceduralType( PROCEDURAL_PRESET_NOT ) , m_OperationFlags( 0 ) , m_pfnAddChannelCallback( NULL ) , m_pfnFinishChannelCallback( NULL ) { Assert( m_pRecordingMgr ); m_TimeSelection = m_pRecordingMgr->GetTimeSelection(); if ( pRecordingMgr ) { m_bInModificationLayer = pRecordingMgr->IsModificationLayerActive(); } } ~CUndoAddRecordingLayer() { // Destroy any paste data which was stored. DestroyPasteData( m_ClipboardData ); // Destroy any log layer data int nChannels = m_ChannelInfo.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { g_pDataModel->DestroyElement( m_ChannelInfo[ iChannel ].m_hRawDataLayer ); } if ( m_pPresetValuesDict ) { delete m_pPresetValuesDict; } } void SaveRecordingLayerData( CRecordingLayer *pRecordingLayer ) { Assert( pRecordingLayer ); // Save the operation type m_pPresetValuesDict = CopyAttributeDict( pRecordingLayer->m_pPresetValuesDict ); m_tHeadShotTime = pRecordingLayer->m_tHeadShotTime; m_ProceduralType = pRecordingLayer->m_ProceduralType; m_OperationFlags = pRecordingLayer->m_OperationFlags; m_pfnAddChannelCallback = pRecordingLayer->m_pfnAddChannelCallback; m_pfnFinishChannelCallback = pRecordingLayer->m_pfnFinishChannelCallback; // Save the operation time parameters m_TimeSelection.m_flIntensity = pRecordingLayer->m_flIntensity; m_TimeSelection.m_flThreshold = pRecordingLayer->m_flThreshold; m_TimeSelection.SetRecordingMode( pRecordingLayer->m_RecordingMode ); // Store the channel information for all of the channels that are modified by the recording layer. This // information is not required for the undo, which simply destroys the recording layer, but is required // for the redo operation as it needs to add the channels to the recording layer when it re-creates it. int nChannels = pRecordingLayer->m_LayerChannels.Count(); m_ChannelInfo.EnsureCount( nChannels ); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { ChannelInfo_t &dstInfo = m_ChannelInfo[ iChannel ]; LayerChannelInfo_t &srcInfo = pRecordingLayer->m_LayerChannels[ iChannel ]; dstInfo.m_Channel = srcInfo.m_Channel; dstInfo.m_ComponentFlags = srcInfo.m_ComponentFlags; dstInfo.m_pPresetValue = srcInfo.m_pPresetValue; dstInfo.m_pPresetTimes = srcInfo.m_pPresetTimes; dstInfo.m_pRoot = srcInfo.m_pRoot; dstInfo.m_pShot = srcInfo.m_pShot; // Copy the transform info dstInfo.m_HeadPosition = srcInfo.m_HeadPosition; dstInfo.m_TransformWriteMode = srcInfo.m_TransformWriteMode; dstInfo.m_bManipulateInFalloff = srcInfo.m_bManipulateInFalloff; dstInfo.m_Transform = srcInfo.m_Transform; dstInfo.m_DeltaRotationLocal = srcInfo.m_DeltaRotationLocal; dstInfo.m_DeltaRotationParent = srcInfo.m_DeltaRotationParent; dstInfo.m_PivotPosition = srcInfo.m_PivotPosition; // Copy the data of the to attribute if ( srcInfo.m_ToAttrData.Size() > 0 ) { dstInfo.m_ToAttrData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); srcInfo.m_ToAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); dstInfo.m_ToAttrData.Put( srcInfo.m_ToAttrData.PeekGet( 0 ), srcInfo.m_ToAttrData.GetBytesRemaining() ); } // Copy the data of the from attribute if ( srcInfo.m_FromAttrData.Size() > 0 ) { dstInfo.m_FromAttrData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); srcInfo.m_FromAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); dstInfo.m_FromAttrData.Put( srcInfo.m_FromAttrData.PeekGet( 0 ), srcInfo.m_FromAttrData.GetBytesRemaining() ); } // If the recording layer had a raw data layer for the channel make a copy of it. if ( srcInfo.m_hRawDataLayer.Get() ) { CDmeChannel *pChannel = srcInfo.m_Channel.Get(); if ( pChannel ) { CDmeLog *pLog = pChannel->GetLog(); if ( pLog ) { dstInfo.m_hRawDataLayer = pLog->AddNewLayer(); dstInfo.m_hRawDataLayer->CopyLayer( srcInfo.m_hRawDataLayer ); pLog->RemoveLayerFromTail(); } } } } // Copy the paste data from the recording layer if it has stored it. CopyPasteData( m_ClipboardData, pRecordingLayer->m_ClipboardData ); } virtual void Undo() { CDmeChannelModificationLayer* pModificationLayer = m_pRecordingMgr->GetModificationLayer(); if ( pModificationLayer ) { pModificationLayer->RemoveLastRecordingLayer(); // If the modification layer is now empty, destroy it completely. if ( pModificationLayer->NumRecordingLayers() == 0 ) { m_pRecordingMgr->FinishModificationLayer( false ); } } } virtual void Redo() { m_pRecordingMgr->SetInRedo( true ); // Create a new modification layer if needed. m_pRecordingMgr->StartModificationLayer( &m_TimeSelection, m_bInModificationLayer ); // Add the recording layer to the modification layer. m_pRecordingMgr->StartLayerRecording( "AddRecordingLayerRedo", m_pPresetValuesDict, m_tHeadShotTime, m_ProceduralType, m_OperationFlags, m_pfnAddChannelCallback, m_pfnFinishChannelCallback ); // Add the channels to the recording layer. int nChannels = m_ChannelInfo.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { ChannelInfo_t &srcInfo = m_ChannelInfo[ iChannel ]; int nChannelIndex = m_pRecordingMgr->AddChannelToRecordingLayer( srcInfo.m_Channel.Get(), srcInfo.m_ComponentFlags, srcInfo.m_pRoot, srcInfo.m_pShot ); if ( nChannelIndex >= 0 ) { LayerChannelInfo_t &dstInfo = m_pRecordingMgr->m_pActiveRecordingLayer->m_LayerChannels[ nChannelIndex ]; // Copy the transform info dstInfo.m_HeadPosition = srcInfo.m_HeadPosition; dstInfo.m_TransformWriteMode = srcInfo.m_TransformWriteMode; dstInfo.m_bManipulateInFalloff = srcInfo.m_bManipulateInFalloff; dstInfo.m_Transform = srcInfo.m_Transform; dstInfo.m_DeltaRotationLocal = srcInfo.m_DeltaRotationLocal; dstInfo.m_DeltaRotationParent = srcInfo.m_DeltaRotationParent; dstInfo.m_PivotPosition = srcInfo.m_PivotPosition; dstInfo.m_ComponentFlags = srcInfo.m_ComponentFlags; dstInfo.m_pPresetValue = srcInfo.m_pPresetValue; dstInfo.m_pPresetTimes = srcInfo.m_pPresetTimes; // Copy the data of the to attribute if ( srcInfo.m_ToAttrData.Size() > 0 ) { dstInfo.m_ToAttrData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); srcInfo.m_ToAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); dstInfo.m_ToAttrData.Put( srcInfo.m_ToAttrData.PeekGet( 0 ), srcInfo.m_ToAttrData.GetBytesRemaining() ); } // Copy the data of the from attribute if ( srcInfo.m_FromAttrData.Size() > 0 ) { dstInfo.m_FromAttrData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); srcInfo.m_FromAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); dstInfo.m_FromAttrData.Put( srcInfo.m_FromAttrData.PeekGet( 0 ), srcInfo.m_FromAttrData.GetBytesRemaining() ); } // If there there is a raw data layer make a copy of it. if ( srcInfo.m_hRawDataLayer.Get() ) { CDmeChannel *pChannel = srcInfo.m_Channel.Get(); if ( pChannel ) { CDmeLog *pLog = pChannel->GetLog(); if ( pLog ) { dstInfo.m_hRawDataLayer = pLog->AddNewLayer(); dstInfo.m_hRawDataLayer->CopyLayer( srcInfo.m_hRawDataLayer ); pLog->RemoveLayerFromTail(); } } } } } // Restore the clipboard data in the recording layer CopyPasteData( m_pRecordingMgr->m_pActiveRecordingLayer->m_ClipboardData, m_ClipboardData ); m_pRecordingMgr->FinishLayerRecording( m_TimeSelection.m_flThreshold, true, false ); m_pRecordingMgr->SetModificationLayerDirty(); m_pRecordingMgr->SetInRedo( false ); } private: struct ChannelInfo_t { CDmeHandle< CDmeChannel > m_Channel; CDmeHandle< CDmeLogLayer, HT_UNDO > m_hRawDataLayer; CUtlBuffer m_ToAttrData; CUtlBuffer m_FromAttrData; DmeTime_t m_HeadPosition; TransformWriteMode_t m_TransformWriteMode; bool m_bManipulateInFalloff; matrix3x4_t m_Transform; Quaternion m_DeltaRotationLocal; Quaternion m_DeltaRotationParent; Vector m_PivotPosition; LogComponents_t m_ComponentFlags; const CDmAttribute *m_pPresetValue; const CDmAttribute *m_pPresetTimes; CDmeClip *m_pRoot; CDmeClip *m_pShot; }; CDmeChannelRecordingMgr *m_pRecordingMgr; DmeLog_TimeSelection_t m_TimeSelection; AttributeDict_t *m_pPresetValuesDict; DmeTime_t m_tHeadShotTime; int m_ProceduralType; int m_OperationFlags; bool m_bInModificationLayer; CUtlVector< ChannelInfo_t > m_ChannelInfo; CUtlVector< KeyValues * > m_ClipboardData; FnRecordChannelCallback m_pfnAddChannelCallback; FnRecordChannelCallback m_pfnFinishChannelCallback; }; //----------------------------------------------------------------------------- // CUndoFinishModificationLayer // // Undo operation for finishing a modification layer, stores all of the // information required to reconstruct the modification layer, redo simply // finishes the modification layer again. Used both for saving and not saving // the changes in the modification layer when finishing. The implementation is // simply to use the functionality of the CUndoAddRecordingLayer to store and // re-create the recording layers of the modification layer. // //----------------------------------------------------------------------------- class CUndoFinishModificationLayer : public CUndoElement { public: CUndoFinishModificationLayer( const char *pUndoDesc, CDmeChannelRecordingMgr* pRecordingMgr, bool saveChanges ) : CUndoElement( pUndoDesc ) , m_pRecordingMgr( pRecordingMgr ) , m_bSaveChanges( saveChanges ) { Assert( m_pRecordingMgr ); if ( m_pRecordingMgr ) { CDmeChannelModificationLayer* pModificationLayer = pRecordingMgr->GetModificationLayer(); if ( pModificationLayer ) { // Save the data required to restore each of the recording layers. int nLayers = pModificationLayer->NumRecordingLayers(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { // Add a new undo operation for the recording layer CUndoAddRecordingLayer *pUndoLayer = new CUndoAddRecordingLayer( "Undo Recoding Layer", m_pRecordingMgr ); m_RecordingLayers.AddToTail( pUndoLayer ); // Save the data from the recording layer in the new undo operation CRecordingLayer &recordingLayer = pModificationLayer->GetRecordingLayer( iLayer ); pUndoLayer->SaveRecordingLayerData( &recordingLayer ); } } } } ~CUndoFinishModificationLayer() { int nLayers = m_RecordingLayers.Count(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { delete m_RecordingLayers[ iLayer ]; m_RecordingLayers[ iLayer ] = NULL; } } virtual void Undo() { // Undoing a finish operation requires reconstructing each of the recording layers, so to undo // the modification layer finish operation we actually redo all of the recording layer operations. int nLayers = m_RecordingLayers.Count(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { Assert( m_RecordingLayers[ iLayer ] ); if ( m_RecordingLayers[ iLayer ] ) { m_RecordingLayers[ iLayer ]->Redo(); } } } virtual void Redo() { // Redoing the modification layer finish is simply a matter of calling finish again, except // this time undo will be disabled. Note we never flatten the layers here because flatten // layers has its own redo and we do not remove layers when not saving changes. m_pRecordingMgr->SetInRedo( true ); m_pRecordingMgr->FinishModificationLayer( m_bSaveChanges, false ); m_pRecordingMgr->SetInRedo( false ); } private: CDmeChannelRecordingMgr *m_pRecordingMgr; bool m_bSaveChanges; CUtlVector< CUndoAddRecordingLayer* > m_RecordingLayers; }; //----------------------------------------------------------------------------- // CUndoSetTimeSelection // // Undo operation for changing setting the time selection. This correctly // updates both the standard time selection and the base time selection. // //----------------------------------------------------------------------------- class CUndoSetTimeSelection : public CUndoElement { public: CUndoSetTimeSelection( const char *pUndoDesc, CDmeChannelRecordingMgr* pRecordingMgr, const DmeLog_TimeSelection_t &newTS, const CUtlVector< TimeSelection_t > &oldBaseTS, const CUtlVector< TimeSelection_t > &newBaseTS ) : CUndoElement( pUndoDesc ) , m_pRecordingMgr( pRecordingMgr ) , m_newTimeSelection( newTS.m_nTimes ) , m_newLeftFalloffType( newTS.m_nFalloffInterpolatorTypes[ 0 ] ) , m_newRightFalloffType( newTS.m_nFalloffInterpolatorTypes[ 1 ] ) , m_newLeftInfinite( newTS.m_bInfinite[ 0 ] ) , m_newRightInfinite( newTS.m_bInfinite[ 1 ] ) { if ( m_pRecordingMgr ) { const DmeLog_TimeSelection_t &orignalTimeSelection = m_pRecordingMgr->GetTimeSelection(); m_originalTimeSelection = orignalTimeSelection.m_nTimes; m_originalLeftFalloffType = orignalTimeSelection.m_nFalloffInterpolatorTypes[ 0 ]; m_originalRightFalloffType = orignalTimeSelection.m_nFalloffInterpolatorTypes[ 1 ]; m_originalLeftInfinite = orignalTimeSelection.m_bInfinite[ 0 ]; m_originalRightInfinite = orignalTimeSelection.m_bInfinite[ 1 ]; m_newBaseTimeSelectionList = newBaseTS; m_originalBaseTimeSelectionList = oldBaseTS; } } virtual void Undo() { if ( m_pRecordingMgr ) { m_pRecordingMgr->UpdateTimeSelection( m_originalTimeSelection, m_originalBaseTimeSelectionList, m_originalLeftFalloffType, m_originalRightFalloffType, m_originalLeftInfinite, m_originalRightInfinite ); } } virtual void Redo() { if ( m_pRecordingMgr ) { m_pRecordingMgr->UpdateTimeSelection( m_newTimeSelection, m_newBaseTimeSelectionList, m_newLeftFalloffType, m_newRightFalloffType, m_newLeftInfinite, m_newRightInfinite ); } } private: CDmeChannelRecordingMgr *m_pRecordingMgr; CUtlVector< TimeSelection_t > m_newBaseTimeSelectionList; TimeSelection_t m_newTimeSelection; int m_newLeftFalloffType; int m_newRightFalloffType; bool m_newLeftInfinite; bool m_newRightInfinite; CUtlVector< TimeSelection_t > m_originalBaseTimeSelectionList; TimeSelection_t m_originalTimeSelection; int m_originalLeftFalloffType; int m_originalRightFalloffType; bool m_originalLeftInfinite; bool m_originalRightInfinite; }; //----------------------------------------------------------------------------- // // CDmeChannelRecordingMgr // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- static CDmeChannelRecordingMgr s_ChannelRecordingMgr; CDmeChannelRecordingMgr *g_pChannelRecordingMgr = &s_ChannelRecordingMgr; //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CDmeChannelRecordingMgr::CDmeChannelRecordingMgr() { m_bSavedUndoState = false; m_bUseTimeSelection = false; m_bModificationLayerDirty = false; m_bModificationProcessing = false; m_bWantsToFinish = false; m_bFinishFlattenLayers = false; m_bModificationLayerEnabled = true; m_bInRedo = false; m_pActiveRecordingLayer = NULL; m_pModificationLayer = NULL; m_nProceduralType = PROCEDURAL_PRESET_NOT; m_pRevealTarget = NULL; m_RandomSeed = 0; m_TransformWriteMode = TRANSFORM_WRITE_MODE_TRANSFORM; } //----------------------------------------------------------------------------- // Purpose: Start a new recording layer, may not be done when there is already // and active recording layer. // Input : pUndoRedoDesc - String to be used as the description for the undo // and redo operations of the recording layer. // Input : operationType - Specification of the general operation which the // recording layer will perform. // Input : proceduralType - If the recording operation is a procedural // operation this may specify the specific type of procedural operation. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::StartLayerRecording( const char * const pUndoRedoDesc, AttributeDict_t *pPresetValuesDict /*= NULL*/, DmeTime_t tHeadShotTime /*= DMETIME_INVALID*/, int proceduralType /*=PROCEDURAL_PRESET_NOT*/, int nFlags /* = 0 */, FnRecordChannelCallback pfnAddChannel /*= NULL*/, FnRecordChannelCallback pfnFinishChannel /*= NULL*/ ) { g_pDataModel->StartUndo( pUndoRedoDesc, pUndoRedoDesc ); m_bSavedUndoState = g_pDataModel->IsUndoEnabled(); Assert( m_pActiveRecordingLayer == NULL ); if ( m_pModificationLayer ) { m_pActiveRecordingLayer = m_pModificationLayer->AddRecordingLayer(); } else { m_pActiveRecordingLayer = new CRecordingLayer; } m_pActiveRecordingLayer->m_pPresetValuesDict = CopyAttributeDict( pPresetValuesDict ); m_pActiveRecordingLayer->m_tHeadShotTime = tHeadShotTime; m_pActiveRecordingLayer->m_ProceduralType = proceduralType; m_pActiveRecordingLayer->m_OperationFlags = nFlags; m_pActiveRecordingLayer->m_pfnAddChannelCallback = pfnAddChannel; m_pActiveRecordingLayer->m_pfnFinishChannelCallback = pfnFinishChannel; m_pActiveRecordingLayer->m_OriginalTimes = m_TimeSelection.m_nTimes; m_pActiveRecordingLayer->m_BaseTimes = m_TimeSelection.m_nTimes; if ( m_bSavedUndoState == true ) { CUndoAddRecordingLayer *pUndo = new CUndoAddRecordingLayer( "AddRecordingLayer", this ); g_pDataModel->AddUndoElement( pUndo ); m_pActiveRecordingLayer->m_pUndoOperation = pUndo; } g_pDataModel->SetUndoEnabled( false ); } //----------------------------------------------------------------------------- // Purpose: Complete the current recording layer, there must be an active // recording layer. // Input : flThreshold - threshold value used in flattening the log layers from // the recording layer on to their proceeding log layers. // Input : bFlattenLayers - Flag indicating if the log layers resulting from // the record operation should be flattened on to the proceeding layer. // Input : bAllowFinishModification - Flag indicating if the recording layer is // allowed to finish the modification layer. Most recording operations // will not finish the modification layer, but some such as the drop // operation will finish the modification layer if this flag is true. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::FinishLayerRecording( float flThreshhold, bool bFlattenLayers /*=true*/, bool bAllowFinishModification /*= true*/ ) { Assert( m_pActiveRecordingLayer ); bool finishModification = false; if ( m_pActiveRecordingLayer ) { m_pActiveRecordingLayer->m_flThreshold = flThreshhold; m_pActiveRecordingLayer->m_flIntensity = m_TimeSelection.m_flIntensity; m_pActiveRecordingLayer->m_RecordingMode = m_TimeSelection.GetRecordingMode(); m_pActiveRecordingLayer->m_RandomSeed = m_RandomSeed; if ( m_pActiveRecordingLayer->m_ProceduralType == PROCEDURAL_PRESET_NOT ) { m_pActiveRecordingLayer->m_ProceduralType = GetProceduralType(); } // Set the flag to for the modification layer to be completed if the operation is to drop the layer. if ( bAllowFinishModification && ( m_pActiveRecordingLayer->m_ProceduralType == PROCEDURAL_PRESET_DROP_LAYER ) ) { finishModification = true; } // If the recording layer is a paste operation that is part of a modification layer it will need store the paste // data so that the operation can be correctly executed even if the contents of the clipboard have been changed. bool pasteOperation = ( m_pActiveRecordingLayer->m_ProceduralType == PROCEDURAL_PRESET_PASTE ); if ( pasteOperation && ( m_pModificationLayer != NULL ) ) { if ( m_pActiveRecordingLayer->m_ClipboardData.Count() == 0) { CUtlVector< KeyValues * > clipBoardList; g_pDataModel->GetClipboardData( clipBoardList ); CopyClipboardDataForRecordingLayer( clipBoardList ); } } // If time is advancing add an additional layer which will hold // the original recorded values not filtered by the time selection. if ( IsTimeAdvancing() && IsModificationLayerActive() ) { int c = m_pActiveRecordingLayer->m_LayerChannels.Count(); for ( int i = 0 ; i < c; ++i ) { LayerChannelInfo_t &channelInfo = m_pActiveRecordingLayer->m_LayerChannels[ i ]; if ( channelInfo.m_hRawDataLayer.Get() == NULL ) { CDmeChannel *pChannel = channelInfo.m_Channel.Get(); if ( !pChannel ) continue; CDmeLog *pLog = pChannel->GetLog(); Assert( pLog ); if ( !pLog ) continue; CDmeLogLayer *pTopLayer = pLog->GetLayer( pLog->GetTopmostLayer() ); channelInfo.m_hRawDataLayer = pLog->AddNewLayer(); channelInfo.m_hRawDataLayer->CopyLayer( pTopLayer ); pLog->RemoveLayerFromTail(); } } } // Save the information from the recording layer that will be required to reconstruct the recording // layer for the redo operation which was not available when the recording layer was created. if ( m_pActiveRecordingLayer->m_pUndoOperation ) { m_pActiveRecordingLayer->m_pUndoOperation->SaveRecordingLayerData( m_pActiveRecordingLayer ); } // Finalize the recording for all channels in the layer and detach the channels from the // recording layer. The channels will no longer know where they are within the recording // layer, but the recording layer will retain the list of channels it operated on. RemoveAllChannelsFromRecordingLayer( m_pActiveRecordingLayer, false ); g_pDataModel->SetUndoEnabled( m_bSavedUndoState ); if ( bFlattenLayers ) { FlattenLayers( m_pActiveRecordingLayer ); } // If there is no modification layer then the recording layer was allocated and should be destroyed. if ( m_pModificationLayer == NULL ) { if ( !m_bInRedo ) { RunFinishCallbacksOnRecordingLayer( m_pActiveRecordingLayer ); } delete m_pActiveRecordingLayer; } if ( finishModification == false ) { g_pDataModel->FinishUndo(); } } m_pActiveRecordingLayer = NULL; m_nProceduralType = PROCEDURAL_PRESET_NOT; m_pRevealTarget = NULL; m_PasteTarget.RemoveAll(); // If the finish modification layer flag was set, finish the modification layer. Some // recording operations such as the drop preset force the completion of the layer. if ( finishModification ) { FinishModificationLayer(); g_pDataModel->FinishUndo(); } } //----------------------------------------------------------------------------- // Purpose: Cancel the current recording layer, losing all of its changes. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::CancelLayerRecording() { if ( m_pActiveRecordingLayer ) { RunFinishCallbacksOnRecordingLayer( m_pActiveRecordingLayer ); // Detach all of the channels from the recording layer and and delete the associate log layers. RemoveAllChannelsFromRecordingLayer( m_pActiveRecordingLayer, !m_bSavedUndoState ); if ( m_bSavedUndoState ) { m_pActiveRecordingLayer = NULL; g_pDataModel->SetUndoEnabled( m_bSavedUndoState ); g_pDataModel->AbortUndoableOperation(); } else { if ( m_pModificationLayer ) { m_pActiveRecordingLayer = NULL; CDisableUndoScopeGuard undosg; // If the modification layer is now empty, destroy it completely. if ( m_pModificationLayer->NumRecordingLayers() < 2 ) { FinishModificationLayer( false ); } else { // Remove the recording layer from the modification layer m_pModificationLayer->RemoveLastRecordingLayer(); } } else { delete m_pActiveRecordingLayer; m_pActiveRecordingLayer = NULL; } } m_nProceduralType = PROCEDURAL_PRESET_NOT; m_pRevealTarget = NULL; m_PasteTarget.RemoveAll(); } } //----------------------------------------------------------------------------- // Purpose: Creates a modification layer on which all further editing will // take place until EndModificationLayer() is called. If there is already an // active modification layer then the time selection of the active modification // layer will be updated, but a new modification layer will not be started. // Input : pTimeSelection - pointer to the active time selection which the // modification layer is to apply to. // Input : createLayer - flag indicating if a modification layer should be // created, if false the function may be used to update the current // time selection. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::StartModificationLayer( const DmeLog_TimeSelection_t *pTimeSelection, bool createLayer /*= true*/ ) { // If the modification layer has already been started just update the time parameters. if ( m_pModificationLayer != NULL ) { if ( pTimeSelection ) { m_TimeSelection = *pTimeSelection; m_TimeSelection.ResetTimeAdvancing(); } return; } // Create the modification layer unless create flag is false. if ( createLayer && m_bModificationLayerEnabled ) { m_pModificationLayer = new CDmeChannelModificationLayer(); } // Set the time selection to provided time selection or reset the time selection // to the default values and disable its use if no time selection was provided. if ( pTimeSelection ) { m_TimeSelection = *pTimeSelection; m_bUseTimeSelection = true; } else { m_TimeSelection = DmeLog_TimeSelection_t(); m_bUseTimeSelection = false; } m_TimeSelection.ResetTimeAdvancing(); } //----------------------------------------------------------------------------- // Purpose: Complete the current modification layer, applying the modifications // to the base log layers of the effected channels and destroying all of the // recording layer data contained within the modification layer. // Input : bSaveChanges - flag indicating if the changes within the // modification layer should be applied or discarded. // Input : bFlattenLayers - flag indicating if the log layers of the // modification should be flattened onto the base layer. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::FinishModificationLayer( bool bSaveChanges, bool bFlattenLayers ) { // Should never call finish modification layer while there is an active // recording layer. FinishLayerRecording() should be called first. Assert( m_pActiveRecordingLayer == NULL ); if ( m_pActiveRecordingLayer != NULL ) { return; } // If the modification layer has already been finished // is is already waiting to finish, ignore the request. if ( IsModificationLayerActive() == false ) { m_bUseTimeSelection = false; m_TimeSelection.ResetTimeAdvancing(); return; } // Make sure the modification layer is not dirty. If it is set the flag which specifies the // modification layer should be finished once the processing is complete. If we are not // going to save the changes ignore the fact that the layer is dirty and just finish it. if ( m_bModificationLayerDirty && bSaveChanges ) { // If this assert is hit finish modification layer has been called twice without the // modification layer being processed or the flags have not been properly cleared. Assert( m_bWantsToFinish == false ); m_bWantsToFinish = true; m_bFinishFlattenLayers = bFlattenLayers; } else { if ( g_pDataModel->IsUndoEnabled() ) { g_pDataModel->StartUndo( "Finish Modification Layer", "Finish Modification Layer" ); CUndoFinishModificationLayer *pUndo = new CUndoFinishModificationLayer( "FinishModificationLayer", this, bSaveChanges ); g_pDataModel->AddUndoElement( pUndo ); } // Destroy the modification layer and the recording layers it contains. if ( m_pModificationLayer ) { // Complete the modification layer and flatten the channel log layers if requested. m_pModificationLayer->Finish( bSaveChanges, bFlattenLayers, !m_bInRedo ); delete m_pModificationLayer; m_pModificationLayer = NULL; } // Complete the undo operation for the modification layer if ( g_pDataModel->IsUndoEnabled() ) { g_pDataModel->FinishUndo(); } // Reset the time selection information m_bUseTimeSelection = false; m_TimeSelection.ResetTimeAdvancing(); // Clear the finish flags m_bWantsToFinish = false; m_bFinishFlattenLayers = false; m_bModificationLayerDirty = false; } } //----------------------------------------------------------------------------- // Purpose: Enable or disable use of the modification layer. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::EnableModificationLayer( bool enable ) { if ( enable ) { m_bModificationLayerEnabled = true; } else { // Finish any active modification layer before disabling. FinishModificationLayer(); m_bModificationLayerEnabled = false; } } //----------------------------------------------------------------------------- // Purpose: Get the current time selection as a CDmeTimeSelection element // Output : timeSelection - reference to the time selection to be returned with // the current time selection values. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::GetTimeSelection( CDmeTimeSelection &timeSelection ) const { timeSelection.SetThreshold( m_TimeSelection.m_flThreshold ); timeSelection.SetCurrent( m_TimeSelection.m_nTimes ); timeSelection.SetFalloffInterpolatorType( 0, m_TimeSelection.m_nFalloffInterpolatorTypes[ 0 ] ); timeSelection.SetFalloffInterpolatorType( 1, m_TimeSelection.m_nFalloffInterpolatorTypes[ 1 ] ); timeSelection.SetResampleInterval( m_TimeSelection.m_nResampleInterval ); if ( m_TimeSelection.m_bInfinite[ 0 ] ) { timeSelection.SetInfinite( 0 ); } if ( m_TimeSelection.m_bInfinite[ 1 ] ) { timeSelection.SetInfinite( 1 ); } } //----------------------------------------------------------------------------- // Purpose: Get the current time selection // Output : Returns a reference to the current time selection structure //----------------------------------------------------------------------------- const DmeLog_TimeSelection_t &CDmeChannelRecordingMgr::GetTimeSelection() const { return m_TimeSelection; } //----------------------------------------------------------------------------- // Purpose: Set the time selection for the modification layer and re-apply all // recording layers that are on the current modification layer with the new // time selection. Must be done with active modification layer, but without an // active recording layer. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::SetTimeSelection( const DmeLog_TimeSelection_t &newTS, bool bUpdateBaseTimes ) { // Can only set the time selection with the modification layer active but without any active recording layers. if ( ( m_pModificationLayer == NULL ) || ( m_pActiveRecordingLayer != NULL ) ) { Assert( m_pModificationLayer != NULL ); Assert( m_pActiveRecordingLayer == NULL ); return; } int nNumRecordingLayers = m_pModificationLayer->NumRecordingLayers(); CUtlVector< TimeSelection_t > newBaseTimeList; CUtlVector< TimeSelection_t > oldBaseTimeList; newBaseTimeList.SetCount( nNumRecordingLayers ); oldBaseTimeList.SetCount( nNumRecordingLayers ); for ( int iRecordingLayer = 0; iRecordingLayer < nNumRecordingLayers; ++iRecordingLayer ) { CRecordingLayer &recordingLayer = m_pModificationLayer->GetRecordingLayer( iRecordingLayer ); TimeSelection_t newBaseTimes = recordingLayer.m_BaseTimes; Assert( newBaseTimes[ TS_LEFT_HOLD ] >= newBaseTimes[ TS_LEFT_FALLOFF ] ); Assert( newBaseTimes[ TS_RIGHT_HOLD ] >= newBaseTimes[ TS_LEFT_HOLD ] ); Assert( newBaseTimes[ TS_RIGHT_FALLOFF ] >= newBaseTimes[ TS_RIGHT_HOLD ] ); // Update the base times by the same amount the time selection is to be changed. if ( bUpdateBaseTimes ) { // As long as the base times are being updated, the ratio of the duration of each section of the base time selection compared to // the visible time selection should remain the same. Additionally movements of the hold region should operate such the source sample // data appears static. This means for a change applied to the visible hold region, the change applied to the base time selection // needs to be multiplied by the hold region ration. Although it would be desirable for the falloff regions to behave this way, if // both the falloff time and the hold times are being modified this is not possible, so the hold region takes precedence. DmeTime_t baseLeftTime = newBaseTimes[ TS_LEFT_HOLD ] - newBaseTimes[ TS_LEFT_FALLOFF ]; DmeTime_t baseHoldTime = newBaseTimes[ TS_RIGHT_HOLD ] - newBaseTimes[ TS_LEFT_HOLD ]; DmeTime_t baseRightTime = newBaseTimes[ TS_RIGHT_FALLOFF ] - newBaseTimes[ TS_RIGHT_HOLD ]; DmeTime_t leftTime = m_TimeSelection.m_nTimes[ TS_LEFT_HOLD ] - m_TimeSelection.m_nTimes[ TS_LEFT_FALLOFF ]; DmeTime_t holdTime = m_TimeSelection.m_nTimes[ TS_RIGHT_HOLD ] - m_TimeSelection.m_nTimes[ TS_LEFT_HOLD ]; DmeTime_t rightTime = m_TimeSelection.m_nTimes[ TS_RIGHT_FALLOFF ] - m_TimeSelection.m_nTimes[ TS_RIGHT_HOLD ]; float flLeftRatio = ( ( leftTime > DMETIME_ZERO ) && ( baseLeftTime > DMETIME_ZERO ) ) ? ( baseLeftTime / leftTime ) : 1.0f; float flHoldRatio = ( ( holdTime > DMETIME_ZERO ) && ( baseHoldTime > DMETIME_ZERO ) ) ? ( baseHoldTime / holdTime ) : 1.0f; float flRightRatio = ( ( rightTime > DMETIME_ZERO ) && ( baseRightTime > DMETIME_ZERO ) ) ? ( baseRightTime / rightTime ) : 1.0f; // Apply the ratio adjusted delta time to the left and right hold times DmeTime_t leftHoldDT = newTS.m_nTimes[ TS_LEFT_HOLD ] - m_TimeSelection.m_nTimes[ TS_LEFT_HOLD ]; DmeTime_t rightHoldDT = newTS.m_nTimes[ TS_RIGHT_HOLD ] - m_TimeSelection.m_nTimes[ TS_RIGHT_HOLD ]; newBaseTimes[ TS_LEFT_HOLD ] += ( leftHoldDT * flHoldRatio ); newBaseTimes[ TS_RIGHT_HOLD ] += ( rightHoldDT * flHoldRatio ); // Update the falloff times so that the ratios are preserved. newBaseTimes[ TS_LEFT_FALLOFF ] = newBaseTimes[ TS_LEFT_HOLD ] - ( ( newTS.m_nTimes[ TS_LEFT_HOLD ] - newTS.m_nTimes[ TS_LEFT_FALLOFF ] ) * flLeftRatio ); newBaseTimes[ TS_RIGHT_FALLOFF ] = newBaseTimes[ TS_RIGHT_HOLD ] + ( ( newTS.m_nTimes[ TS_RIGHT_FALLOFF ] - newTS.m_nTimes[ TS_RIGHT_HOLD ] ) * flRightRatio ); // Make sure the time selection times remain in order. newBaseTimes[ TS_LEFT_FALLOFF ] = MIN( newBaseTimes[ TS_LEFT_FALLOFF ], newBaseTimes[ TS_LEFT_HOLD ] - DMETIME_MINDELTA ); newBaseTimes[ TS_RIGHT_HOLD ] = MAX( newBaseTimes[ TS_RIGHT_HOLD ], newBaseTimes[ TS_LEFT_HOLD ] ); newBaseTimes[ TS_RIGHT_FALLOFF ] = MAX( newBaseTimes[ TS_RIGHT_FALLOFF ], newBaseTimes[ TS_RIGHT_HOLD ] + DMETIME_MINDELTA ); } oldBaseTimeList[ iRecordingLayer ] = recordingLayer.m_BaseTimes; newBaseTimeList[ iRecordingLayer ] = newBaseTimes; recordingLayer.m_BaseTimes = newBaseTimes; } if ( g_pDataModel->IsUndoEnabled() ) { CUndoSetTimeSelection *pUndo = new CUndoSetTimeSelection( "SetTimeSelection", this, newTS, oldBaseTimeList, newBaseTimeList ); g_pDataModel->AddUndoElement( pUndo ); } // Set the new time selection for which the effects of the recording layers are to be applied. m_TimeSelection.m_nTimes = newTS.m_nTimes; m_TimeSelection.m_nFalloffInterpolatorTypes[ 0 ] = newTS.m_nFalloffInterpolatorTypes[ 0 ]; m_TimeSelection.m_nFalloffInterpolatorTypes[ 1 ] = newTS.m_nFalloffInterpolatorTypes[ 1 ]; m_TimeSelection.m_bInfinite[ 0 ] = newTS.m_bInfinite[ 0 ]; m_TimeSelection.m_bInfinite[ 1 ] = newTS.m_bInfinite[ 1 ]; m_bUseTimeSelection = true; m_bModificationLayerDirty = true; } //----------------------------------------------------------------------------- // Update the time selection state, this is used by undo //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::UpdateTimeSelection( const TimeSelection_t &timeSelection, const CUtlVector< TimeSelection_t > &baseTimeSelectionList, int leftFalloff, int rightFalloff, bool bLeftInfinite, bool bRightInfinite ) { m_TimeSelection.m_nTimes = timeSelection; m_TimeSelection.m_nFalloffInterpolatorTypes[ 0 ] = leftFalloff; m_TimeSelection.m_nFalloffInterpolatorTypes[ 1 ] = rightFalloff; m_TimeSelection.m_bInfinite[ 0 ] = bLeftInfinite; m_TimeSelection.m_bInfinite[ 1 ] = bRightInfinite; m_bUseTimeSelection = true; m_bModificationLayerDirty = true; int nNumRecordingLayers = m_pModificationLayer->NumRecordingLayers(); Assert( baseTimeSelectionList.Count() == nNumRecordingLayers ); if ( baseTimeSelectionList.Count() == nNumRecordingLayers ) { for ( int iRecordingLayer = 0; iRecordingLayer < nNumRecordingLayers; ++iRecordingLayer ) { CRecordingLayer &recordingLayer = m_pModificationLayer->GetRecordingLayer( iRecordingLayer ); recordingLayer.m_BaseTimes = baseTimeSelectionList[ iRecordingLayer ]; } } } //----------------------------------------------------------------------------- // Purpose: Start or continue processing of the recording layers within the // modification layer. // In/Out : recordingLayer - Input with the index of the recording layer to // process, returns the index of the next recording layer to be // processed. // Output : proceduralType - returns the specific procedural preset type for // procedural recording operations. // Output : operationFlags - returns flags providing information about the // operation of the recording layer. // Output : randomSeed - returns the random seed used by the recording layer // for procedural operations which depend on random values. // Output : flIntensity - returns the intensity parameter of the recording layer // which was processed. // Output : Returns true if recording layer index is valid and the recording // layer was processed, returns false if the recording layer index was // out of range, which occurs when processing of all layers has been // completed. //----------------------------------------------------------------------------- CRecordingLayer *CDmeChannelRecordingMgr::ProcessModificationLayer( int &recordingLayer ) { // Do nothing if the modification layer does not require an update. if ( !m_bModificationLayerDirty ) return NULL; // Obviously the modification layer must exist in order to process it. if ( m_pModificationLayer == NULL ) { Assert( m_pModificationLayer ); return NULL; } // If all of the recording layers have been processed clear the dirty // flag and return false to indicate no further processing is required. if ( recordingLayer >= m_pModificationLayer->NumRecordingLayers() ) { m_bModificationLayerDirty = false; m_bModificationProcessing = false; // If the flag specifying that the modification layer should be finished is set, call // the finish function to flatten the layers and complete the modification layer. if ( m_bWantsToFinish ) { m_bWantsToFinish = false; FinishModificationLayer( m_bFinishFlattenLayers ); } return NULL; } // Perform initialization actions if ( recordingLayer == 0 ) { // Set the flag to indicate that the modification layer is currently being processed. m_bModificationProcessing = true; // Restore the modification log layer of each of the channels in the modification layer to their // original state before any recording layers were applied by copying the base layer. m_pModificationLayer->WipeChannelModifications(); } // Get the current recording layer and perform the initial update for applying it to the modification layer. CRecordingLayer &recLayer = m_pModificationLayer->GetRecordingLayer( recordingLayer++ ); ApplyRecordingLayer( recLayer ); return &recLayer; } //----------------------------------------------------------------------------- // Purpose: Complete the modification layer processing of the current recording // layer. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::CompleteModificationProcessing() { Assert( m_pActiveRecordingLayer ); // Record the channel operations if the operation mode requires it. Paste does not // as the values are applied to the channel outside of the record mechanism. if ( m_pActiveRecordingLayer->m_ProceduralType != PROCEDURAL_PRESET_PASTE ) { int c = m_pActiveRecordingLayer->m_LayerChannels.Count(); for ( int i = 0 ; i < c; ++i ) { LayerChannelInfo_t &channelInfo = m_pActiveRecordingLayer->m_LayerChannels[ i ]; CDmeChannel *pChannel = channelInfo.m_Channel.Get(); if ( pChannel ) { pChannel->Operate(); } } } // Finish the record operation by detaching the channels from the record // layer, returning them to play mode, and flattening the newly created layer. RemoveAllChannelsFromRecordingLayer( m_pActiveRecordingLayer, false ); FlattenLayers( m_pActiveRecordingLayer ); // Reset the active recording layer to NULL since the recording // layer being applied is not actually being recorded. m_pActiveRecordingLayer = NULL; m_nProceduralType = PROCEDURAL_PRESET_NOT; m_pRevealTarget = NULL; m_PasteTarget.RemoveAll(); } //----------------------------------------------------------------------------- // Purpose: Apply the effects of the recording layer to its channels with the // active time selection. // Input : recordingLayer - Reference to the recording layer to apply. // Output : Returns the procedural type of the recording layer. //----------------------------------------------------------------------------- bool CDmeChannelRecordingMgr::ApplyRecordingLayer( CRecordingLayer &recordingLayer ) { // An existing recording layer cannot be applied to the the modification // layer while there is currently an active recording layer. if ( m_pActiveRecordingLayer != NULL ) { Assert( m_pActiveRecordingLayer == NULL ); return false; } // Temporarily set the recording layer being re-applied as the active recording layer, this // is done because the channel Operate() function assumes an active recording channel. m_pActiveRecordingLayer = &recordingLayer; m_nProceduralType = recordingLayer.m_ProceduralType; m_RandomSeed = recordingLayer.m_RandomSeed; m_TimeSelection.m_flIntensity = recordingLayer.m_flIntensity; m_TimeSelection.SetRecordingMode( recordingLayer.m_RecordingMode ); // Iterate through the channels in the recording layer, add a new layer to // the log of each channel and put the channel in recording mode so that // the operation of the layer can be recorded into the log. int c = recordingLayer.m_LayerChannels.Count(); for ( int i = 0 ; i < c; ++i ) { LayerChannelInfo_t &channelInfo = recordingLayer.m_LayerChannels[ i ]; CDmeChannel *pChannel = channelInfo.m_Channel.Get(); if ( !pChannel ) continue; CDmeLog *pLog = pChannel->GetLog(); Assert( pLog ); if ( !pLog ) continue; // Add the new layer to the log and assign the channel // its location within the current recoding layer. CDmeLogLayer* pNewLayer = pLog->AddNewLayer(); if ( pNewLayer == NULL ) continue; pNewLayer->SetInfinite( m_TimeSelection.m_bInfinite[ 0 ], m_TimeSelection.m_bInfinite[ 1 ] ); pChannel->SetRecordLayerIndex( i ); // If the recording operation was an attribute over time, use the stored recorded // log data to re-apply the recording operation for the current time frame. if ( channelInfo.m_hRawDataLayer.Get() ) { DmeLog_TimeSelection_t localTimeSelection = m_TimeSelection; localTimeSelection.m_flIntensity = 1.0f; for ( int i = 0; i < TS_TIME_COUNT; ++i ) { localTimeSelection.m_nTimes[i] = channelInfo.m_ClipStack.ToChildMediaTime( localTimeSelection.m_nTimes[i], false ); } pLog->BlendLayersUsingTimeSelection( pLog->GetLayer( channelInfo.m_BaseLayer ), channelInfo.m_hRawDataLayer, pNewLayer, localTimeSelection, true, true, true, DMETIME_ZERO ); // Do not put the channel into record mode. continue; } if ( recordingLayer.m_ProceduralType == PROCEDURAL_PRESET_NOT ) { // If the recording layer's operation was not a procedural operation, restore the value of // the source attribute to the recording value before re-recording with the new time frame. CDmAttribute *pFromAttr = pChannel->GetFromAttribute(); if ( pFromAttr ) { channelInfo.m_FromAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); if ( IsArrayType( pFromAttr->GetType() ) ) { pFromAttr->UnserializeElement( pChannel->GetFromArrayIndex(), channelInfo.m_FromAttrData ); } else { pFromAttr->Unserialize( channelInfo.m_FromAttrData ); } } CDmAttribute *pToAttr = pChannel->GetToAttribute(); if ( pToAttr ) { channelInfo.m_ToAttrData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); if ( IsArrayType( pToAttr->GetType() ) ) { pToAttr->UnserializeElement( pChannel->GetToArrayIndex(), channelInfo.m_ToAttrData ); } else { pToAttr->Unserialize( channelInfo.m_ToAttrData ); } } // Restore the transform, delta rotation, and pivot values in the control to the values used in the original operation. CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pChannel->GetFromElement() ); if ( pTransformControl ) { pTransformControl->SetManipulationTransform( channelInfo.m_Transform ); pTransformControl->SetManipulationRotationLocal( channelInfo.m_DeltaRotationLocal ); pTransformControl->SetManipulationRotationParent( channelInfo.m_DeltaRotationParent ); pTransformControl->SetManipulationPivot( channelInfo.m_PivotPosition ); } } // Record the operation. pChannel->SetMode( CM_RECORD ); } return true; } //----------------------------------------------------------------------------- // Purpose: Store the data of the provided attribute in the channel's recording // data. // Input : nChannelIndex - Index of the channel within the recording layer's // array of active channels. // Input : fromAttr - flag indicating if the data of the from attribute is to // be stored ( true ) or the data of the to attribute is to be stored // ( false ). //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::StoreChannelAttributeData( int nChannelIndex, bool fromAttr ) { Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer ) { LayerChannelInfo_t& info = m_pActiveRecordingLayer->m_LayerChannels[ nChannelIndex ]; CDmeChannel *pChannel = info.m_Channel.Get(); // The data from the provided attribute may be stored in either the to or from attribute // data for the channel, select the appropriate data buffer based on selection flag. CUtlBuffer *pBuffer = fromAttr ? &info.m_FromAttrData : &info.m_ToAttrData; CDmAttribute *pAttr = fromAttr ? pChannel->GetFromAttribute() : pChannel->GetToAttribute(); if ( pAttr ) { // Clear the buffer, we only want to store a single value. pBuffer->Clear(); // If storing the to attribute data, make sure it is current and has the // actual value of the log, not the control default value if the log is empty. if ( !fromAttr ) { pChannel->Play( true ); } if ( IsArrayType( pAttr->GetType() ) ) { int nArrayIndex = fromAttr ? pChannel->GetFromArrayIndex() : pChannel->GetToArrayIndex(); pAttr->SerializeElement( nArrayIndex, *pBuffer ); } else { pAttr->Serialize( *pBuffer ); } } // If saving the from attribute, save the transform, delta rotation and pivot information. if ( fromAttr ) { CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pChannel->GetFromElement() ); if ( pTransformControl ) { pTransformControl->GetManipulationTransform( info.m_Transform ); pTransformControl->GetManipulationRotationLocal( info.m_DeltaRotationLocal ); pTransformControl->GetManipulationRotationParent( info.m_DeltaRotationParent ); pTransformControl->GetManipulationPivot( info.m_PivotPosition ); } } } } //----------------------------------------------------------------------------- // Purpose: Adds a channel to the recording layer // Input : pChannel - pointer to the channel to be added to the recording // layer. // Input : componentFlags - flags specifying which components of the channel // are to be recorded. // Input : pRoot - pointer to the current root clip ( movie ) // Input : pShot - pointer to the current shot // Output : Returns - Index of the location in the recording layer's array of // channel information to which the new channel was assigned. //----------------------------------------------------------------------------- int CDmeChannelRecordingMgr::AddChannelToRecordingLayer( CDmeChannel *pChannel, LogComponents_t componentFlags, CDmeClip *pRoot, CDmeClip *pShot ) { Assert( pChannel ); if ( pChannel == NULL ) return -1; // The channel must not already be in an active recording layer. Assert( pChannel->GetRecordLayerIndex() == -1 ); // There must be an active recording layer before channels may be added. Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer == NULL ) return -1; CDmeLog *pLog = pChannel->GetLog(); if ( !pLog ) return -1; // If specified call the add channel callback before adding the channel to the recording layer. if ( !m_bInRedo && ( m_pActiveRecordingLayer->m_pfnAddChannelCallback ) ) { CEnableUndoScopeGuard undosg; m_pActiveRecordingLayer->m_pfnAddChannelCallback( pChannel ); } int nRecordLayerIndex = m_pActiveRecordingLayer->m_LayerChannels.AddToTail(); LayerChannelInfo_t& info = m_pActiveRecordingLayer->m_LayerChannels[nRecordLayerIndex]; info.m_Channel = pChannel; info.m_pRoot = pRoot; info.m_pShot = pShot; info.m_ComponentFlags = componentFlags; info.m_HeadPosition = pChannel->GetCurrentTime(); info.m_TransformWriteMode = m_TransformWriteMode; info.m_bManipulateInFalloff = m_TimeSelection.m_bManipulateInFalloff; if ( !m_bInRedo && pLog->IsEmpty() ) { CEnableUndoScopeGuard undosg; pLog->SetKey( pChannel->GetCurrentTime(), pChannel->GetFromAttribute(), pChannel->GetFromArrayIndex() ); } // Add the channel to the modification layer, which results in a new layer being // added to the log on which subsequent modifications to the log will take place. if ( m_pModificationLayer ) { info.m_ModLayerIndex = m_pModificationLayer->AddChannel( pChannel, m_bSavedUndoState ); } // Store the index of the current top most layer, this is the modification layer // which was just added. When flatting the recording layer it will be flattened // only to the modification layer, not all the way to the base layer of the log. info.m_BaseLayer = pLog->GetTopmostLayer(); if ( pRoot ) { if ( !pChannel->BuildClipStack( &info.m_ClipStack, pRoot, pShot ) ) { m_pActiveRecordingLayer->m_LayerChannels.Remove( nRecordLayerIndex ); return -1; } } // Store the index of the the channel in the recording layer for obtaining // access to the channel info in the recording layer from the channel. pChannel->SetRecordLayerIndex( nRecordLayerIndex ); // Store the value of the To attribute before recording starts // to use as a reference value when re-applying the operation. StoreChannelAttributeData( nRecordLayerIndex, false ); bool bWasUndoEnabled = false; if ( m_bSavedUndoState ) { bWasUndoEnabled = g_pDataModel->IsUndoEnabled(); g_pDataModel->SetUndoEnabled( true ); } CDmeLogLayer *pNewLayer = pLog->AddNewLayer(); if ( pNewLayer ) { pNewLayer->SetInfinite( m_TimeSelection.m_bInfinite[ 0 ], m_TimeSelection.m_bInfinite[ 1 ] ); } if ( m_bSavedUndoState ) { g_pDataModel->SetUndoEnabled( bWasUndoEnabled ); } pChannel->SetMode( CM_RECORD ); return nRecordLayerIndex; } //----------------------------------------------------------------------------- // Explicitly set the clipboard data for the active recording layer //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::CopyClipboardDataForRecordingLayer( const CUtlVector< KeyValues * > &keyValuesList ) { if ( m_pActiveRecordingLayer == NULL ) return; // Destroy any existing clipboard data DestroyPasteData( m_pActiveRecordingLayer->m_ClipboardData ); // Copy the new clipboard data CopyPasteData( m_pActiveRecordingLayer->m_ClipboardData, keyValuesList ); } //----------------------------------------------------------------------------- // Purpose: Removes all channels from the recording layer // Input : pRecordingLayer - Pointer to the recording layer from which all the // channel information is to be removed. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::RemoveAllChannelsFromRecordingLayer( CRecordingLayer *pRecordingLayer, bool destroyLogLayers ) { Assert( pRecordingLayer ); if ( pRecordingLayer == NULL ) return; int c = pRecordingLayer->m_LayerChannels.Count(); for ( int i = 0 ; i < c; ++i ) { CDmeChannel *pChannel = pRecordingLayer->m_LayerChannels[ i ].m_Channel.Get(); if ( !pChannel ) continue; CDmeLog *pLog = pChannel->GetLog(); if ( pLog ) { if ( IsUsingTimeSelection() ) { // Computes local times for the time selection DmeLog_TimeSelection_t timeSelection; GetLocalTimeSelection( timeSelection, pChannel->GetRecordLayerIndex() ); pLog->FinishTimeSelection( pChannel->GetCurrentTime(), timeSelection ); } // Release recording layer from the log. if ( destroyLogLayers ) { pLog->RemoveLayerFromTail(); } } pChannel->SetRecordLayerIndex( -1 ); pChannel->SetMode( CM_PLAY ); } } //----------------------------------------------------------------------------- // Flattens recorded layers into the base layer //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::FlattenLayers( CRecordingLayer *pRecordingLayer ) { Assert( pRecordingLayer ); if ( pRecordingLayer == NULL ) return; int nFlags = 0; if ( IsUsingDetachedTimeSelection() && IsTimeAdvancing() ) { nFlags |= CDmeLog::FLATTEN_NODISCONTINUITY_FIXUP; } int c = pRecordingLayer->m_LayerChannels.Count(); for ( int i = 0 ; i < c; ++i ) { LayerChannelInfo_t &channelInfo = pRecordingLayer->m_LayerChannels[ i ]; CDmeChannel *pChannel = channelInfo.m_Channel.Get(); if ( !pChannel ) continue; CDmeLog *pLog = pChannel->GetLog(); Assert( pLog ); if ( !pLog ) continue; if ( channelInfo.m_BaseLayer < 0 ) continue; pLog->FlattenLayers( pRecordingLayer->m_flThreshold, nFlags, channelInfo.m_BaseLayer ); } } //----------------------------------------------------------------------------- // Calls the callback for all channels in the specified recording layer //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::RunFinishCallbacksOnRecordingLayer( CRecordingLayer *pRecordingLayer ) { if ( pRecordingLayer == NULL ) return; if ( pRecordingLayer->m_pfnFinishChannelCallback == NULL ) return; int nNumChannels = pRecordingLayer->m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nNumChannels; ++iChannel ) { LayerChannelInfo_t &channelInfo = pRecordingLayer->m_LayerChannels[ iChannel ]; CDmeChannel *pChannel = channelInfo.m_Channel.Get(); if ( pChannel == NULL ) continue; pRecordingLayer->m_pfnFinishChannelCallback( pChannel ); } } //----------------------------------------------------------------------------- // Used to iterate over all channels currently being recorded //----------------------------------------------------------------------------- int CDmeChannelRecordingMgr::GetLayerRecordingChannelCount() { if ( m_pActiveRecordingLayer ) { return m_pActiveRecordingLayer->m_LayerChannels.Count(); } return 0; } CDmeChannel* CDmeChannelRecordingMgr::GetLayerRecordingChannel( int nIndex ) { if ( m_pActiveRecordingLayer ) { Assert( nIndex < m_pActiveRecordingLayer->m_LayerChannels.Count() ); if ( nIndex < m_pActiveRecordingLayer->m_LayerChannels.Count() ) { return m_pActiveRecordingLayer->m_LayerChannels[ nIndex ].m_Channel.Get(); } } return NULL; } //----------------------------------------------------------------------------- // Computes time selection info in log time for a particular recorded channel //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::GetLocalTimeSelection( DmeLog_TimeSelection_t& selection, int nIndex ) { Assert( m_bUseTimeSelection ); Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer ) { LayerChannelInfo_t& info = m_pActiveRecordingLayer->m_LayerChannels[nIndex]; CDmeChannel *pChannel = info.m_Channel.Get(); Assert( pChannel ); if ( pChannel == NULL ) return; selection = m_TimeSelection; for ( int i = 0; i < TS_TIME_COUNT; ++i ) { selection.m_nTimes[i] = info.m_ClipStack.ToChildMediaTime( selection.m_nTimes[i], false ); } selection.m_pPresetValue = info.m_pPresetValue; selection.m_pPresetTimes = info.m_pPresetTimes; selection.m_tHeadPosition = info.m_HeadPosition; selection.m_TransformWriteMode = info.m_TransformWriteMode; selection.m_nComponentFlags = info.m_ComponentFlags; selection.m_bManipulateInFalloff = info.m_bManipulateInFalloff; // If the time selection is marked as infinite, make sure it extents past the ends of the log CDmeLog *pLog = pChannel->GetLog(); if ( pLog && selection.m_bInfinite[ 0 ] ) { selection.m_nTimes[ TS_LEFT_HOLD ] = MIN( pLog->GetBeginTime() , selection.m_nTimes[ TS_LEFT_HOLD ] ); selection.m_nTimes[ TS_LEFT_FALLOFF ] = selection.m_nTimes[ TS_LEFT_HOLD ]; } if ( pLog && selection.m_bInfinite[ 1 ] ) { selection.m_nTimes[ TS_RIGHT_HOLD ] = MAX( pLog->GetEndTime(), selection.m_nTimes[ TS_RIGHT_HOLD ] ); selection.m_nTimes[ TS_RIGHT_FALLOFF ] = selection.m_nTimes[ TS_RIGHT_HOLD ]; } // If the modification layer is currently being processed set the pointer to the channel's to attribute // to be used as the old head position, this is required because the value of the head position now may // not be what it was when the recording first took place. if ( m_bModificationProcessing ) { CDmAttribute *pAttr = pChannel->GetToAttribute(); if ( pAttr ) { selection.m_pOldHeadValue = pAttr; if ( IsArrayType( pAttr->GetType() ) ) { selection.m_OldHeadValueIndex = pChannel->GetToArrayIndex(); } } } } } //----------------------------------------------------------------------------- // Get the index of the modification base layer for the specified channel // within the current recording layer. //----------------------------------------------------------------------------- int CDmeChannelRecordingMgr::GetModificationBaseLayer( int nIndex ) { Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer ) { LayerChannelInfo_t& info = m_pActiveRecordingLayer->m_LayerChannels[ nIndex ]; return info.m_BaseLayer; } return -1; } //----------------------------------------------------------------------------- // Methods which control various aspects of recording //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::UpdateTimeAdvancing( bool bPaused, DmeTime_t tCurTime ) { Assert( m_bUseTimeSelection ); Assert( m_pActiveRecordingLayer ); if ( !bPaused && !m_TimeSelection.IsTimeAdvancing() && m_pActiveRecordingLayer ) { m_TimeSelection.StartTimeAdvancing(); // blow away logs after curtime int nCount = m_pActiveRecordingLayer->m_LayerChannels.Count(); for ( int i = 0; i < nCount; ++i ) { LayerChannelInfo_t& info = m_pActiveRecordingLayer->m_LayerChannels[i]; DmeTime_t t = info.m_ClipStack.ToChildMediaTime( tCurTime, false ); info.m_Channel->GetLog()->RemoveKeys( t, DMETIME_MAXTIME ); } } } //----------------------------------------------------------------------------- // Update the head positions of all of the channels in the active recording // layer. The input time should be a global time which will be converted to the // local time for each channel. //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::UpdateRecordingChannelHeadPositions( DmeTime_t tCurTime ) { Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer ) { int nChannels = m_pActiveRecordingLayer->m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { LayerChannelInfo_t &info = m_pActiveRecordingLayer->m_LayerChannels[ iChannel ]; DmeTime_t channelTime = info.m_ClipStack.ToChildMediaTime( tCurTime, false ); if ( info.m_HeadPosition != channelTime ) { info.m_HeadPosition = channelTime; CDmeChannel *pChannel = info.m_Channel.Get(); if ( pChannel ) { pChannel->SetCurrentTime( channelTime ); } // Update the stored To attribute value to match the value at the new head position. StoreChannelAttributeData( iChannel, false ); } } } } void CDmeChannelRecordingMgr::UpdateRecordingTimeSelectionTimes( const DmeLog_TimeSelection_t& timeSelection ) { Assert( m_pActiveRecordingLayer ); m_TimeSelection.m_nTimes = timeSelection.m_nTimes; for ( int i = 0; i < 2; ++i ) { m_TimeSelection.m_nFalloffInterpolatorTypes[ i ] = timeSelection.m_nFalloffInterpolatorTypes[ i ]; m_TimeSelection.m_bInfinite[ i ] = timeSelection.m_bInfinite[ i ]; } m_TimeSelection.m_nResampleInterval = timeSelection.m_nResampleInterval; } void CDmeChannelRecordingMgr::SetIntensityOnAllLayers( float flIntensity ) { m_TimeSelection.m_flIntensity = flIntensity; } void CDmeChannelRecordingMgr::SetRecordingMode( RecordingMode_t mode ) { m_TimeSelection.SetRecordingMode( mode ); } void CDmeChannelRecordingMgr::SetPresetValue( CDmeChannel* pChannel, const CDmAttribute *pPresetValue, const CDmAttribute *pPresetTimes ) { Assert( m_pActiveRecordingLayer ); if ( m_pActiveRecordingLayer == NULL ) return; Assert( pChannel->GetRecordLayerIndex() != -1 ); if ( pChannel->GetRecordLayerIndex() == -1 ) return; LayerChannelInfo_t &info = m_pActiveRecordingLayer->m_LayerChannels[ pChannel->GetRecordLayerIndex() ]; info.m_pPresetValue = pPresetValue; info.m_pPresetTimes = pPresetTimes; } void CDmeChannelRecordingMgr::SetInRedo( bool bInRedo ) { Assert( bInRedo != m_bInRedo ); m_bInRedo = bInRedo; } //----------------------------------------------------------------------------- // Methods to query aspects of recording //----------------------------------------------------------------------------- bool CDmeChannelRecordingMgr::IsUsingDetachedTimeSelection() const { Assert( m_pActiveRecordingLayer ); return !m_TimeSelection.m_bAttachedMode; } bool CDmeChannelRecordingMgr::IsTimeAdvancing() const { Assert( m_pActiveRecordingLayer ); return m_TimeSelection.IsTimeAdvancing(); } bool CDmeChannelRecordingMgr::IsUsingTimeSelection() const { return m_bUseTimeSelection; } bool CDmeChannelRecordingMgr::IsRecordingLayerActive() const { return ( m_pActiveRecordingLayer != NULL ); } bool CDmeChannelRecordingMgr::IsModificationLayerActive() const { return ( ( m_pModificationLayer != NULL ) && ( m_bWantsToFinish == false ) ); } bool CDmeChannelRecordingMgr::IsModificationLayerVisible() const { if ( !IsModificationLayerActive() ) return false; return m_pModificationLayer->IsVisible(); } bool CDmeChannelRecordingMgr::IsProcessingModifications() const { return m_bModificationProcessing; } bool CDmeChannelRecordingMgr::IsModificationLayerEnabled() const { return m_bModificationLayerEnabled; } bool CDmeChannelRecordingMgr::ShouldRecordUsingTimeSelection() const { return m_bUseTimeSelection; } bool CDmeChannelRecordingMgr::IsInRedo() const { return m_bInRedo; } void CDmeChannelRecordingMgr::SetProceduralTarget( int nProceduralMode, const CDmAttribute *pTarget ) { m_nProceduralType = nProceduralMode; m_pRevealTarget = pTarget; m_PasteTarget.RemoveAll(); m_RandomSeed = 0; } void CDmeChannelRecordingMgr::SetProceduralTarget( int nProceduralMode, const CUtlVector< KeyValues * >& list, int randomSeed ) { m_nProceduralType = nProceduralMode; m_pRevealTarget = NULL; m_PasteTarget.RemoveAll(); for ( int i = 0; i < list.Count(); ++i ) { m_PasteTarget.AddToTail( list[ i ] ); } m_RandomSeed = randomSeed; } int CDmeChannelRecordingMgr::GetProceduralType() const { return m_nProceduralType; } const CDmAttribute *CDmeChannelRecordingMgr::GetProceduralTarget() const { Assert( m_pRevealTarget ); return m_pRevealTarget; } const CUtlVector< KeyValues * > &CDmeChannelRecordingMgr::GetPasteTarget() const { return m_PasteTarget; } void CDmeChannelRecordingMgr::SetModificationLayerDirty() { if ( m_pModificationLayer ) { m_bModificationLayerDirty = true; } } CDmeChannelModificationLayer *CDmeChannelRecordingMgr::GetModificationLayer() { return m_pModificationLayer; } void CDmeChannelRecordingMgr::SetTransformWriteMode( TransformWriteMode_t mode ) { m_TransformWriteMode = mode; if ( m_pModificationLayer ) { m_pModificationLayer->UpdateTransformWriteMode( mode ); SetModificationLayerDirty(); } } TransformWriteMode_t CDmeChannelRecordingMgr::GetTransformWriteMode() const { return m_TransformWriteMode; } void CDmeChannelRecordingMgr::UpdateActiveLayerManipulateInFalloff( bool bManipulateInFalloff ) { if ( m_pActiveRecordingLayer ) { int nChannels = m_pActiveRecordingLayer->m_LayerChannels.Count(); for ( int iChannel = 0; iChannel < nChannels; ++iChannel ) { LayerChannelInfo_t &channelInfo = m_pActiveRecordingLayer->m_LayerChannels[ iChannel ]; channelInfo.m_bManipulateInFalloff = bManipulateInFalloff; } } } //----------------------------------------------------------------------------- // Copy the clipboard data for the paste operation //----------------------------------------------------------------------------- void CDmeChannelRecordingMgr::GetPasteClipboardData( CUtlVector< KeyValues * > &list ) const { if ( m_pActiveRecordingLayer ) { if ( m_pActiveRecordingLayer->m_ClipboardData.Count() > 0 ) { int nKeys = m_pActiveRecordingLayer->m_ClipboardData.Count(); for ( int iKey = 0; iKey < nKeys; ++iKey ) { list.AddToTail( m_pActiveRecordingLayer->m_ClipboardData[ iKey ] ); } return; } } g_pDataModel->GetClipboardData( list ); } DmeTime_t GetGlobalKeyTime( const DmeClipStack_t &clipStack, CDmeLog *pLog, int idx ) { return pLog && idx < pLog->GetKeyCount() ? clipStack.FromChildMediaTime( pLog->GetKeyTime( idx ), false ) : DMETIME_MAXTIME; } //----------------------------------------------------------------------------- // Expose this class to the scene database //----------------------------------------------------------------------------- IMPLEMENT_ELEMENT_FACTORY( DmeChannel, CDmeChannel ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CDmeChannel::OnConstruction() { m_nRecordLayerIndex = -1; m_nNextCurveType = CURVE_DEFAULT; m_fromElement .Init( this, "fromElement", FATTRIB_HAS_CALLBACK | FATTRIB_NEVERCOPY ); m_fromAttribute .Init( this, "fromAttribute", FATTRIB_TOPOLOGICAL | FATTRIB_HAS_CALLBACK ); m_fromIndex .Init( this, "fromIndex", FATTRIB_TOPOLOGICAL ); m_toElement .Init( this, "toElement", FATTRIB_HAS_CALLBACK | FATTRIB_NEVERCOPY ); m_toAttribute .Init( this, "toAttribute", FATTRIB_TOPOLOGICAL | FATTRIB_HAS_CALLBACK ); m_toIndex .Init( this, "toIndex", FATTRIB_TOPOLOGICAL ); m_mode .InitAndSet( this, "mode", (int)CM_PASS ); m_log .Init( this, "log" ); m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; } void CDmeChannel::OnDestruction() { } int CDmeChannel::GetFromArrayIndex() const { return m_fromIndex; } int CDmeChannel::GetToArrayIndex() const { return m_toIndex; } int CDmeChannel::GetRecordLayerIndex() const { return m_nRecordLayerIndex; } void CDmeChannel::SetRecordLayerIndex( int layerIndex ) { // If this assert is hit it most likely means that the // channel is a being assigned to two record layers at once. Assert( ( layerIndex == -1 ) || ( m_nRecordLayerIndex == -1 ) ); m_nRecordLayerIndex = layerIndex; } void CDmeChannel::Play( bool useEmptyLog /*= false*/ ) { CDmAttribute *pToAttr = GetToAttribute(); if ( pToAttr == NULL ) return; CDmeLog *pLog = GetLog(); if ( !pLog ) return; DmeTime_t time = GetCurrentTime(); DmeTime_t t0 = pLog->GetBeginTime(); DmeTime_t tn = pLog->GetEndTime(); PlayMode_t pmode = PM_HOLD; switch ( pmode ) { case PM_HOLD: time = clamp( time, t0, tn ); break; case PM_LOOP: if ( tn == t0 ) { time = t0; } else { time -= t0; time = time % ( tn - t0 ); time += t0; } break; } // We might not want to do it this way, but this makes empty logs not get in the way if there is a valid pFromAttr if ( !useEmptyLog ) { if ( pLog->IsEmpty() && !pLog->HasDefaultValue() && GetFromAttribute() != NULL ) { Pass(); return; } } pLog->GetValue( time, pToAttr, m_toIndex.Get() ); } void CDmeChannel::Pass() { CDmAttribute *pFromAttr = GetFromAttribute(); CDmAttribute *pToAttr = GetToAttribute(); if ( !pFromAttr || !pToAttr ) return; if ( pFromAttr == pToAttr ) return; DmAttributeType_t type = pFromAttr->GetType(); const void *pValue = NULL; if ( IsArrayType( type ) ) { CDmrGenericArray array( pFromAttr ); pValue = array.GetUntyped( m_fromIndex.Get() ); type = ArrayTypeToValueType( type ); } else { pValue = pFromAttr->GetValueUntyped(); } if ( IsArrayType( pToAttr->GetType() ) ) { CDmrGenericArray array( pToAttr ); array.Set( m_toIndex.Get(), type, pValue ); } else { pToAttr->SetValue( type, pValue ); } } //----------------------------------------------------------------------------- // IsDirty - ie needs to operate //----------------------------------------------------------------------------- bool CDmeChannel::IsDirty() { if ( BaseClass::IsDirty() ) return true; switch( GetMode() ) { case CM_PLAY: return true; case CM_RECORD: if ( m_nRecordLayerIndex != -1 ) return true; // NOTE: Fall through! case CM_PASS: { CDmAttribute *pFromAttr = GetFromAttribute(); if ( pFromAttr && pFromAttr->IsFlagSet( FATTRIB_OPERATOR_DIRTY ) ) return true; } break; default: break; } return false; } void CDmeChannel::Operate() { VPROF( "CDmeChannel::Operate" ); switch ( GetMode() ) { case CM_OFF: return; case CM_PLAY: Play(); return; case CM_RECORD: Record(); return; case CM_PASS: Pass(); return; } } void CDmeChannel::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs ) { ChannelMode_t mode = GetMode(); if ( mode == CM_OFF || mode == CM_PLAY ) return; // off and play ignore inputs CDmAttribute *pAttr = GetFromAttribute(); if ( pAttr != NULL ) { attrs.AddToTail( pAttr ); } } void CDmeChannel::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs ) { ChannelMode_t mode = GetMode(); if ( mode == CM_OFF ) return; // off ignores inputs if ( mode == CM_RECORD || mode == CM_PASS ) { if ( GetFromAttribute() == GetToAttribute() ) return; // record/pass from and to the same attribute doesn't write anything } CDmAttribute *pAttr = GetToAttribute(); if ( pAttr != NULL ) { attrs.AddToTail( pAttr ); } } //----------------------------------------------------------------------------- // accessors //----------------------------------------------------------------------------- CDmElement *CDmeChannel::GetFromElement() const { return m_fromElement; } CDmElement *CDmeChannel::GetToElement() const { return m_toElement; } void CDmeChannel::SetLog( CDmeLog *pLog ) { m_log = pLog; } CDmeLog *CDmeChannel::CreateLog( DmAttributeType_t type ) { CDmeLog *log = CDmeLog::CreateLog( type, GetFileId() ); m_log.Set( log ); return log; } // HACK: This is an evil hack since the element and attribute change sequentially, but they really need to change in lockstep or else you're looking // up an attribute from some other element or vice versa. void CDmeChannel::SetInput( CDmElement* pElement, const char* pAttribute, int index ) { m_fromElement.Set( pElement ); m_fromAttribute.Set( pAttribute ); m_fromIndex.Set( index ); SetupFromAttribute(); } void CDmeChannel::SetOutput( CDmElement* pElement, const char* pAttribute, int index ) { m_toElement.Set( pElement ); m_toAttribute.Set( pAttribute ); m_toIndex.Set( index ); SetupToAttribute(); } void CDmeChannel::SetInput( CDmAttribute *pAttribute, int index ) { if ( pAttribute ) { SetInput( pAttribute->GetOwner(), pAttribute->GetName(), index ); } else { SetInput( NULL, "", index ); } } void CDmeChannel::SetOutput( CDmAttribute *pAttribute, int index ) { if ( pAttribute ) { SetOutput( pAttribute->GetOwner(), pAttribute->GetName(), index ); } else { SetOutput( NULL, "", index ); } } ChannelMode_t CDmeChannel::GetMode() { return static_cast< ChannelMode_t >( m_mode.Get() ); } void CDmeChannel::SetMode( ChannelMode_t mode ) { if ( mode != m_mode ) { m_mode.Set( static_cast< int >( mode ) ); m_TimeState.m_tPreviousTime = DMETIME_INVALID; } } void CDmeChannel::ClearLog() { GetLog()->ClearKeys(); } CDmeLog *CDmeChannel::GetLog() { if ( !m_log.GetElement() && ( m_FromAttributeHandle == DMATTRIBUTE_HANDLE_INVALID ) ) { // NOTE: This will generate a new log based on the from attribute SetupFromAttribute(); } return m_log.GetElement(); } //----------------------------------------------------------------------------- // Used to cache off handles to attributes //----------------------------------------------------------------------------- CDmAttribute *CDmeChannel::SetupFromAttribute() { m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; CDmElement *pObject = m_fromElement.GetElement(); const char *pName = m_fromAttribute.Get(); if ( pObject == NULL || pName == NULL || !pName[0] ) return NULL; CDmAttribute *pAttr = pObject->GetAttribute( pName ); if ( !pAttr ) return NULL; m_FromAttributeHandle = pAttr->GetHandle(); DmAttributeType_t fromType = pAttr->GetType(); if ( IsArrayType( fromType ) ) { fromType = ArrayTypeToValueType( fromType ); } CDmeLog *pLog = m_log.GetElement(); if ( pLog == NULL ) { CreateLog( fromType ); return pAttr; } DmAttributeType_t logType = pLog->GetDataType(); if ( IsArrayType( logType ) ) { logType = ArrayTypeToValueType( logType ); } if ( logType != fromType ) { // NOTE: This will release the current log CreateLog( fromType ); } return pAttr; } CDmAttribute *CDmeChannel::SetupToAttribute() { m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; CDmElement *pObject = m_toElement.GetElement(); const char *pName = m_toAttribute.Get(); if ( pObject == NULL || pName == NULL || !pName[0] ) return NULL; CDmAttribute *pAttr = pObject->GetAttribute( pName ); if ( !pAttr ) return NULL; m_ToAttributeHandle = pAttr->GetHandle(); return pAttr; } //----------------------------------------------------------------------------- // This function gets called whenever an attribute changes //----------------------------------------------------------------------------- void CDmeChannel::OnAttributeChanged( CDmAttribute *pAttribute ) { if ( ( pAttribute == m_fromElement .GetAttribute() ) || ( pAttribute == m_fromAttribute.GetAttribute() ) ) { // NOTE: This will force a recache of the attribute handle m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; return; } if ( ( pAttribute == m_toElement .GetAttribute() ) || ( pAttribute == m_toAttribute.GetAttribute() ) ) { m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID; return; } BaseClass::OnAttributeChanged( pAttribute ); } DmeTime_t CDmeChannel::GetCurrentTime() const { return m_TimeState.m_tCurrentTime; } //----------------------------------------------------------------------------- // Simple version. Only works if multiple active channels clips // do not reference the same channels //----------------------------------------------------------------------------- void CDmeChannel::SetCurrentTime( DmeTime_t time ) { m_TimeState.m_tPreviousTime = m_TimeState.m_tCurrentTime; m_TimeState.m_tCurrentTime = time; m_TimeState.m_timeOutsideTimeframe = DMETIME_ZERO; } const TimeState_t &CDmeChannel::GetTimeState() const { return m_TimeState; } void CDmeChannel::SetTimeState( TimeState_t &TimeState ) { m_TimeState = TimeState; } //----------------------------------------------------------------------------- // SetCurrentTime sets the current time on the clip, // choosing the time closest to (and after) a timeframe if multiple sets in a frame //----------------------------------------------------------------------------- void CDmeChannel::SetCurrentTime( DmeTime_t time, DmeTime_t start, DmeTime_t end ) { m_TimeState.m_tPreviousTime = m_TimeState.m_tCurrentTime; DmeTime_t dt( 0 ); if ( time < start ) { dt = time - start; time = start; } else if ( time >= end ) { dt = time - end; time = end; } DmeTime_t totf = m_TimeState.m_timeOutsideTimeframe; const DmeTime_t t0( 0 ); if ( ( dt < t0 && totf < t0 && dt < totf ) || // both prior to clip, old totf closer ( dt < t0 && totf >= t0 ) || // new dt prior to clip, old totf in or after ( dt >= t0 && totf >= t0 && dt > totf ) ) // both after clip, old totf closer return; // if old todt is a better match, don't update channel time m_TimeState.m_tCurrentTime = time; m_TimeState.m_timeOutsideTimeframe = dt; } //----------------------------------------------------------------------------- // ClearTimeMetric marks m_timeOutsideTimeframe invalid (-inf is the worst possible match) //----------------------------------------------------------------------------- void CDmeChannel::ClearTimeMetric() { m_TimeState.m_timeOutsideTimeframe = DmeTime_t::MinTime(); } void CDmeChannel::SetChannelToPlayToSelf( const char *outputAttributeName, float defaultValue, bool force /*= false*/ ) { if ( !HasAttribute( outputAttributeName ) ) { AddAttribute( outputAttributeName, AT_FLOAT ); } CDmeTypedLog< bool > *log = static_cast< CDmeTypedLog< bool >* >( GetLog() ); // Usually we won't put it into playback if it's empty, we'll just read the default value continously if ( force || ( log && !log->IsEmpty() && !log->HasDefaultValue() ) ) { SetMode( CM_PLAY ); SetOutput( this, outputAttributeName ); } SetValue( outputAttributeName, defaultValue ); } void CDmeChannel::SetNextKeyCurveType( int nCurveType ) { m_nNextCurveType = nCurveType; } CDmeLogLayer *FindLayerInSnapshot( const CDmrElementArray& snapshotArray, CDmeLog *origLog ) { if ( !snapshotArray.IsValid() ) return NULL; int c = snapshotArray.Count(); for ( int i = 0; i < c; ++i ) { CDmeLogLayer *layer = CastElement< CDmeLogLayer >( snapshotArray[ i ] ); if ( !layer ) continue; CDmeLog *pLog = layer->GetValueElement< CDmeLog >( "origLog" ); if ( !pLog ) { Assert( 0 ); continue; } if ( pLog == origLog ) return layer; } return NULL; } KeyValues *FindLayerInPasteData( const CUtlVector< KeyValues * > &list, CDmeLog *log ) { int c = list.Count(); for ( int i = 0; i < c; ++i ) { CDisableUndoScopeGuard noundo; KeyValues *kv = list[ i ]; Assert( kv ); if ( Q_stricmp( kv->GetName(), "ControlLayers" ) ) continue; LayerSelectionData_t *data = reinterpret_cast< LayerSelectionData_t * >( kv->GetPtr( "LayerData" ) ); if ( !data ) continue; CDmeChannel *ch = data->m_hChannel; if ( !ch ) continue; CDmeLog *chLog = ch->GetLog(); if ( chLog == log ) return kv; } return NULL; } static int FindSpanningLayerAndSetIntensity( DmeLog_TimeSelection_t &ts, LayerSelectionData_t *data ) { Assert( data->m_vecData.Count() >= 2 ); float frac = ts.m_flIntensity; int i = 0; for ( ; i < data->m_vecData.Count() - 1; ++i ) { LayerSelectionData_t::DataLayer_t *current = &data->m_vecData[ i ]; LayerSelectionData_t::DataLayer_t *next = &data->m_vecData[ i + 1 ]; if ( frac >= current->m_flStartFraction && frac <= next->m_flStartFraction ) { frac = RemapVal( frac, current->m_flStartFraction, next->m_flStartFraction, 0.0f, 1.0f ); ts.m_flIntensity = frac; break; } } return i; } //----------------------------------------------------------------------------- // Purpose: Copy the paste data from the source key values list into the // destination key values list. This makes a complete copy of all data so the // destination can be safely referenced when the source has been modified or // destroyed. // Input : srcList - Reference to the source list of key values to be copied // Output : dstList - Reference to the list to which the key values of the the // source list are to be copied. //----------------------------------------------------------------------------- void CopyPasteData( CUtlVector< KeyValues * > &dstList, const CUtlVector< KeyValues * > &srcList ) { // Assuming the list is empty, if not we could be improperly accumulating data. Assert( dstList.Count() == 0 ); int nKeys = srcList.Count(); for ( int iKey = 0; iKey < nKeys; ++iKey ) { KeyValues *pKeyValue = srcList[ iKey ]; // Skip null key values, but there should not be any. if ( pKeyValue == NULL ) { Assert( pKeyValue ); continue; } // Skip any unknown key value sets if ( V_stricmp( pKeyValue->GetName(), "ControlLayers" ) ) { continue; } // Get the layer selection data from the key value, if there is no LayerData entry, then skip the key. LayerSelectionData_t *pSrcLayerData = reinterpret_cast< LayerSelectionData_t * >( pKeyValue->GetPtr( "LayerData" ) ); if ( pSrcLayerData == NULL ) { continue; } // Get the pointer to the log to which the layer data is for. CDmeLog *pLog = pSrcLayerData->m_hLog.Get(); if ( pLog == NULL ) { Assert( pLog ); continue; } // Create the new layer selection structure and create a new // key value set for it and place it in the destination list. LayerSelectionData_t *pDstLayerData = new LayerSelectionData_t; KeyValues *pDstKeyValues = new KeyValues( "ControlLayers" ); dstList.AddToTail( pDstKeyValues ); // Add the layer data structure pointer to the key value set. pDstKeyValues->SetPtr( "LayerData", reinterpret_cast< void * >( pDstLayerData ) ); // Copy the simple data element of the layer selection data pDstLayerData->m_hChannel = pSrcLayerData->m_hChannel; pDstLayerData->m_hOwner = pSrcLayerData->m_hOwner; pDstLayerData->m_hShot = pSrcLayerData->m_hShot; pDstLayerData->m_hLog = pSrcLayerData->m_hLog; pDstLayerData->m_DataType = pSrcLayerData->m_DataType; pDstLayerData->m_nTimes[ 0 ] = pSrcLayerData->m_nTimes[ 0 ]; pDstLayerData->m_nTimes[ 1 ] = pSrcLayerData->m_nTimes[ 1 ]; pDstLayerData->m_nTimes[ 2 ] = pSrcLayerData->m_nTimes[ 2 ]; pDstLayerData->m_nTimes[ 3 ] = pSrcLayerData->m_nTimes[ 3 ]; // Copy each of the log layers in the layer selection data. int nLayers = pSrcLayerData->m_vecData.Count(); for ( int iLayer = 0; iLayer < nLayers; ++iLayer ) { float srcFrac = pSrcLayerData->m_vecData[ iLayer ].m_flStartFraction; CDmeLogLayer *pSrcLayer = pSrcLayerData->m_vecData[ iLayer ].m_hData.Get(); if ( pSrcLayer ) { // Create a new layer and copy the source layer CDmeLogLayer* pNewLayer = pLog->AddNewLayer(); pNewLayer->CopyLayer( pSrcLayer ); // Add the new layer to the layer selection data LayerSelectionData_t::DataLayer_t dataLayer( srcFrac, pNewLayer ); pDstLayerData->m_vecData.AddToTail( dataLayer ); // Disconnect the new layer from log pLog->RemoveLayerFromTail(); } } } // For iKey } //----------------------------------------------------------------------------- // Purpose: Destroy the contents of the provided paste data list, freeing all // associated resources. // Input : list - Paste data key value list to be destroyed. //----------------------------------------------------------------------------- void DestroyPasteData( CUtlVector< KeyValues * > &list ) { int c = list.Count(); for ( int i = 0; i < c; ++i ) { KeyValues *kv = list[ i ]; Assert( kv ); if ( !kv ) continue; LayerSelectionData_t *data = reinterpret_cast< LayerSelectionData_t * >( kv->GetPtr( "LayerData" ) ); if ( !data ) continue; data->Release(); delete data; } } void CDmeChannel::Record() { VPROF( "CDmeChannel::Record" ); CDmElement* pElement = GetFromElement(); CDmAttribute *pFromAttr = GetFromAttribute(); if ( pFromAttr == NULL ) return; // or clear out the log? CDmeLog *pLog = GetLog(); DmeTime_t time = GetCurrentTime(); if ( m_TimeState.m_tPreviousTime == DMETIME_INVALID ) { m_TimeState.m_tPreviousTime = time; } if ( g_pChannelRecordingMgr->ShouldRecordUsingTimeSelection() ) { Assert( m_nRecordLayerIndex != -1 ); if ( m_nRecordLayerIndex == -1 ) return; // Computes local times for the time selection DmeLog_TimeSelection_t timeSelection; g_pChannelRecordingMgr->GetLocalTimeSelection( timeSelection, m_nRecordLayerIndex ); int nType = g_pChannelRecordingMgr->GetProceduralType(); enum { PROCEDURAL_PRESET_ANIMATED = NUM_PROCEDURAL_PRESET_TYPES + 1 }; if ( nType == PROCEDURAL_PRESET_NOT && timeSelection.m_pPresetTimes ) { nType = PROCEDURAL_PRESET_ANIMATED; // this is an animated preset, which is blended in like paste/spline/etc } switch ( nType ) { default: case PROCEDURAL_PRESET_NOT: { DmeLogTransformParams_t transformParams; transformParams.m_nProceduralType = nType; // If the element is a transform control, get the additional manipulation parameters from the // transform control and store them in the transform parameters so they can be used by the log. CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pElement ); if ( pTransformControl ) { pTransformControl->GetManipulationTransform( transformParams.m_Transform ); pTransformControl->GetManipulationRotationLocal( transformParams.m_RotationLocal ); pTransformControl->GetManipulationRotationParent( transformParams.m_RotationParent ); pTransformControl->GetManipulationPivot( transformParams.m_Pivot ); CDmeChannel* pRotChannel = pTransformControl->GetOrientationChannel(); if ( pRotChannel ) { CDmeLog* pLog = pRotChannel->GetLog(); if ( pLog && pLog->GetDataType() == AT_QUATERNION ) { transformParams.m_pRotationLog = static_cast< CDmeTypedLog< Quaternion >* >( pLog ); } } } else { // Only transform controls can have manipulation in falloff timeSelection.m_bManipulateInFalloff = false; } // Stamp the key at the current head position, re-sampling the current time selection if time is not moving or writing // a key at the current time if time is moving. If the modification layer is active and the time is moving forward the // key will be recorded without respect to the current time selection as the time selection will be applied later. g_pChannelRecordingMgr->StoreChannelAttributeData( m_nRecordLayerIndex, true ); pLog->StampKeyAtHead( time, m_TimeState.m_tPreviousTime, timeSelection, transformParams, pFromAttr, m_fromIndex.Get(), !g_pChannelRecordingMgr->IsModificationLayerActive() ); } break; case PROCEDURAL_PRESET_REVEAL: { // Find the matching layer in the "target" data array const CDmrElementArray snapshotArray = const_cast< CDmAttribute * >( g_pChannelRecordingMgr->GetProceduralTarget() ); CDmeLogLayer *snapshotLayer = FindLayerInSnapshot( snapshotArray, pLog ); if ( snapshotLayer ) { Assert( pLog ); pLog->RevealUsingTimeSelection( timeSelection, snapshotLayer ); } } break; case PROCEDURAL_PRESET_DROP_LAYER: { int nLayers = pLog->GetNumLayers(); // There must be at least 3 layers in order for the drop to operate: // the base layer, the modification layer and the active recording layer. if ( nLayers >= 3 ) { CDmeLogLayer* pRecLayer = pLog->GetLayer( nLayers - 1 ); CDmeLogLayer* pModLayer = pLog->GetLayer( nLayers - 2 ); CDmeLogLayer* pBaseLayer = pLog->GetLayer( nLayers - 3 ); pLog->BlendLayersUsingTimeSelection( pBaseLayer, pModLayer, pRecLayer, timeSelection, true, false, false, DMETIME_ZERO ); } } break; case PROCEDURAL_PRESET_INOUT: case PROCEDURAL_PRESET_JITTER: case PROCEDURAL_PRESET_SMOOTH: case PROCEDURAL_PRESET_SHARPEN: case PROCEDURAL_PRESET_SOFTEN: case PROCEDURAL_PRESET_STAGGER: case PROCEDURAL_PRESET_HOLD: case PROCEDURAL_PRESET_RELEASE: case PROCEDURAL_PRESET_STEADY: case PROCEDURAL_PRESET_PASTE: case PROCEDURAL_PRESET_SPLINE: case PROCEDURAL_PRESET_ANIMATED: { const CUtlVector< KeyValues * > &pasteTarget = g_pChannelRecordingMgr->GetPasteTarget(); KeyValues *layer = FindLayerInPasteData( pasteTarget, pLog ); if ( layer ) { LayerSelectionData_t *data = reinterpret_cast< LayerSelectionData_t * >( layer->GetPtr( "LayerData" ) ); Assert( data ); DmeLog_TimeSelection_t blendTimeSelction = timeSelection; int iSourceLayer = FindSpanningLayerAndSetIntensity( blendTimeSelction, data ); CDmeLogLayer *sourceLayer = data->m_vecData[ iSourceLayer ].m_hData.Get(); CDmeLogLayer *targetLayer = data->m_vecData[ iSourceLayer + 1 ].m_hData.Get(); if ( sourceLayer && sourceLayer->GetKeyCount() > 0 && targetLayer && targetLayer->GetKeyCount() > 0 && sourceLayer->GetKeyCount() == targetLayer->GetKeyCount() ) { Assert( pLog->GetNumLayers() >= 2 ); CDmeLogLayer *outputLayer = pLog->GetLayer( pLog->GetTopmostLayer() ); if ( IsPresetTimeOperation( nType ) ) // stagger and steady operate on sample times mostly { bool bFeatherBlendInFalloff = nType == PROCEDURAL_PRESET_STAGGER || nType == PROCEDURAL_PRESET_STEADY; pLog->BlendTimesUsingTimeSelection( sourceLayer, targetLayer, outputLayer, blendTimeSelction, data->m_nTimes[ TS_LEFT_FALLOFF ], bFeatherBlendInFalloff ); } else if ( iSourceLayer != 0 ) { CDmeLogLayer *baseLayer = data->m_vecData[ 0 ].m_hData.Get(); pLog->BlendLayersUsingTimeSelection( baseLayer, sourceLayer, targetLayer, outputLayer, blendTimeSelction, false, data->m_nTimes[ TS_LEFT_FALLOFF ] ); } else { pLog->BlendLayersUsingTimeSelection( sourceLayer, targetLayer, outputLayer, blendTimeSelction, false, true, true, data->m_nTimes[ TS_LEFT_FALLOFF ] ); } } } } break; } } else { int bestLayer = pLog->GetTopmostLayer(); SegmentInterpolation_t interpSetting = (bestLayer == -1) ? SEGMENT_INTERPOLATE : pLog->GetLayer( bestLayer )->GetSegmentInterpolationSetting( m_TimeState.m_tPreviousTime, DMETIME_INVALID, false ); if( interpSetting == SEGMENT_INTERPOLATE ) //use existing behavior { if ( m_TimeState.m_tPreviousTime != time ) { pLog->SetDuplicateKeyAtTime( m_TimeState.m_tPreviousTime ); } pLog->SetKey( time, pFromAttr, m_fromIndex.Get(), SEGMENT_INTERPOLATE, m_nNextCurveType ); } else { //attempt to preserve the non-interpolated segment as it is. I believe that this will also work for the general case above. But I'm not 100% sure at the moment. Best not to break existing behavior if ( m_TimeState.m_tPreviousTime != time ) { pLog->InsertKeyAtTime( m_TimeState.m_tPreviousTime ); } pLog->SetKey( time, pFromAttr, m_fromIndex.Get(), pLog->GetLayer( bestLayer )->GetSegmentInterpolationSetting( time ), m_nNextCurveType ); } m_nNextCurveType = CURVE_DEFAULT; } // Output the data that's in the log Play(); } //----------------------------------------------------------------------------- // Builds a clip stack that passes through root + shot // Returns true if it succeeded //----------------------------------------------------------------------------- bool CDmeChannel::BuildClipStack( DmeClipStack_t *pClipStack, CDmeClip *pRoot, CDmeClip *pShot ) const { DmAttributeReferenceIterator_t it; for ( it = g_pDataModel->FirstAttributeReferencingElement( GetHandle() ); it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; it = g_pDataModel->NextAttributeReferencingElement( it ) ) { CDmAttribute *pAttribute = g_pDataModel->GetAttribute( it ); CDmElement *pElement = pAttribute->GetOwner(); CDmeChannelsClip *pChannelsClip = CastElement< CDmeChannelsClip >( pElement ); if ( !pChannelsClip ) continue; if ( pChannelsClip->BuildClipStack( pClipStack, pRoot, pShot ) ) return true; } return false; } //----------------------------------------------------------------------------- // Finds the owner clip for a channel which passes through the root //----------------------------------------------------------------------------- CDmeClip* CDmeChannel::FindOwnerClipForChannel( CDmeClip *pRoot ) { DmeClipStack_t stack; if ( BuildClipStack( &stack, pRoot, pRoot ) ) return stack.GetClip( stack.GetClipCount() - 1 ); return NULL; } void CDmeChannel::ScaleSampleTimes( float scale ) { m_log->ScaleSampleTimes( scale ); } void CDmeChannel::ClearAndAddSampleAtTime( DmeTime_t time ) { m_log->ClearAndAddSampleAtTime( time ); } void RemapFloatLogValues( CDmeChannel *pChannel, float flBias, float flScale ) { if ( !pChannel ) return; CDmeLog *pLog = pChannel->GetLog(); if ( !pLog ) return; CDmeTypedLogLayer< float > *pLogLayer = CastElement< CDmeTypedLogLayer< float > >( pLog->GetLayer( pLog->GetTopmostLayer() ) ); if ( !pLogLayer ) return; int nKeys = pLogLayer->GetKeyCount(); for ( int i = 0; i < nKeys; ++i ) { float flValue = pLogLayer->GetKeyValue( i ); pLogLayer->SetKeyValue( i, flValue * flScale + flBias ); } } CDmeChannel *FindChannelTargetingAttribute( CDmAttribute *pTargetAttr ) { if ( !pTargetAttr ) return NULL; CDmElement *pTarget = pTargetAttr->GetOwner(); if ( !pTarget ) return NULL; for ( DmAttributeReferenceIterator_t it = g_pDataModel->FirstAttributeReferencingElement( pTarget->GetHandle() ); it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; it = g_pDataModel->NextAttributeReferencingElement( it ) ) { CDmAttribute *pAttr = g_pDataModel->GetAttribute( it ); Assert( pAttr ); CDmeChannel *pChannel = CastElement< CDmeChannel >( pAttr->GetOwner() ); if ( pChannel && pChannel->GetToAttribute() == pTargetAttr ) return pChannel; } return NULL; }