csgo-2018-source/movieobjects/dmechannel.cpp
2021-07-24 21:11:47 -07:00

3282 lines
107 KiB
C++

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