//====== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======= // // Declaration of CDmeGraphEditorState, a data model element which stores // the active state data for the graph editor. // //============================================================================= #ifndef DMEGRAPHEDITORCURVE_H #define DMEGRAPHEDITORCURVE_H #ifdef _WIN32 #pragma once #endif #include "movieobjects/dmechannel.h" #include "datamodel/dmelement.h" #include "checksum_crc.h" enum SelectionMode_t { SELECT_SET, SELECT_ADD, SELECT_REMOVE, SELECT_TOGGLE, }; enum AddKeyMode_t { ADD_KEYS_AUTO, ADD_KEYS_INTERPOLATE, ADD_KEYS_STEPPED, }; enum TangentOperation_t { TANGENTS_FLAT, // Tangents are modified so the value of the end points are equal to the key value TANGENTS_LINEAR, // Tangents are modified so the endpoints are on the line between the key values TANGENTS_SPLINE, // Tangents are modified so the that they are parallel to the line from the proceeding key to the next key TANGENTS_UNIFIED, // Tangents are modified so both sides of the tangent lie on the same line TANGENTS_ISOMETRIC, // Tangents are modified so both sides of the tangent are of equal length TANGENTS_STEP, // Set the key to step mode, out tangent is ignored and all values between the key and the next key are equal to the key value TANGENTS_WEIGHTED, // Set the key to perform tangent calculations using the time of of the tangents TANGENTS_UNWEIGHTED, // Set the key to perform tangent calculations such that the delta time of the tangents is ignored ( always 1/3 time to next key ). }; enum KeyTangentMode_t { TANGENT_MODE_DEFAULT, TANGENT_MODE_LINEAR, TANGENT_MODE_SPLINE, TANGENT_MODE_STEPPED }; class CDmeGraphEditorCurve; //----------------------------------------------------------------------------- // The CDmeCurveKey class represents a single key on a curve an hold // information about the position of the key and its tangents. The tangent // values are stored as delta so they do not have to be updated when the value // of the key changes. //----------------------------------------------------------------------------- class CDmeCurveKey : public CDmElement { DEFINE_ELEMENT( CDmeCurveKey, CDmElement ); enum KeyDirtyFlags_t { KEY_CLEAN = 0, KEY_IN_DIRTY = 1, KEY_OUT_DIRTY = 2 }; static const int UNWEIGHTED_DISPLAY_LENGTH = 60; public: // Initialize the key with the specified time and value void Initialize( DmeTime_t time, float flValue, int nComponent ); // Get a pointer to the curve to which the key belongs CDmeGraphEditorCurve *GetCurve() const; // Return the sort value for the two keys based on their times static int SortFunc( CDmeCurveKey * const *pKeyA, CDmeCurveKey * const *pKeyB ); // Clear the selection state of the key and its tangents void ClearSelection(); // Set the key tangents to be flat void SetTangentsFlat( const CDmeCurveKey *pPrevKey, const CDmeCurveKey *pNextKey, bool bSetIn, bool bSetOut ); // Set the key tangents to be linear void SetTangentsLinear( const CDmeCurveKey *pPrevKey, const CDmeCurveKey *pNextKey, bool bSetIn, bool bSetOut ); // Set the key tangents using a spline method void SetTangentsSpline( const CDmeCurveKey *pPrevKey, const CDmeCurveKey *pNextKey, bool bSetIn, bool bSetOut ); // Make both of the tangents of the key lie one the same line void SetTangentsUnified( float flUnitsPerSecond, bool bSetIn, bool bSetOut ); // Make both of the tangents of the key be the same length void SetTangentsIsometric( float flUnitsPerSecond, bool bSetIn, bool bSetOut ); // Set the key tangents as being broken (not unified) void SetTangentsBroken(); // Set the key as being in stepped mode void SetStepped( bool bStepped ); // Enable or disable weighted tangents void SetWeighted( bool bWeighted, const CDmeCurveKey *pPrevKey, const CDmeCurveKey *pNextKey ); // Make the specified key match the unweighted form void ConformUnweighted( const CDmeCurveKey *pPrevKey, const CDmeCurveKey *pNextKey ); void SetSelected( bool bSelect ) { m_Selected = bSelect; } void SetInSelected( bool bSelect ) { m_InSelected = bSelect; } void SetOutSelected( bool bSelect ) { m_OutSelected = bSelect; } void SetKeyClean() { m_KeyDirtyFlags = KEY_CLEAN; } void SetKeyInClean() { m_KeyDirtyFlags &= ~KEY_IN_DIRTY; } void SetKeyOutClean() { m_KeyDirtyFlags &= ~KEY_OUT_DIRTY;} void SetKeyInDirty() { m_KeyDirtyFlags |= KEY_IN_DIRTY; } void SetKeyOutDirty() { m_KeyDirtyFlags |= KEY_OUT_DIRTY; } // Tangent manipulation void SetInTime( DmeTime_t dt ) { m_InTime = dt; } void SetInDelta( float flDelta ) { m_InDelta = flDelta; } void SetOutTime( DmeTime_t dt ) { m_OutTime = dt; } void SetOutDelta( float flDelta ) { m_OutDelta = flDelta; } // Accessors bool IsSelected() const { return m_Selected; } bool InTangentSelected() const { return m_InSelected; } bool OutTangentSelected() const { return m_OutSelected; } bool IsKeyWeighted() const { return m_Weighted; } bool IsKeyUnified() const { return m_Unified; } int GetComponent() const { return m_Component; } DmeTime_t GetTime() const { return m_Time; } float GetValue() const { return m_Value; } DmeTime_t GetInTime() const { return m_InTime; } float GetInDelta() const { return m_InDelta; } DmeTime_t GetOutTime() const { return m_OutTime; } float GetOutDelta() const { return m_OutDelta; } bool IsKeyStepped() const { return m_OutMode == TANGENT_MODE_STEPPED; } KeyTangentMode_t GetInMode() const { return ( KeyTangentMode_t )m_InMode.Get(); } KeyTangentMode_t GetOutMode() const { return ( KeyTangentMode_t )m_OutMode.Get(); } bool IsInTangentValid() const { return ( m_InTime != DMETIME_ZERO ); } bool IsOutTangentValid() const { return ( m_OutTime != DMETIME_ZERO ); } bool IsKeyDirty() const { return ( m_KeyDirtyFlags != KEY_CLEAN ); } bool IsKeyInDirty() const { return ( m_KeyDirtyFlags & KEY_IN_DIRTY ) != 0; } bool IsKeyOutDirty() const { return ( m_KeyDirtyFlags & KEY_OUT_DIRTY ) != 0; } static int GetUnweightedDisplayLength() { return UNWEIGHTED_DISPLAY_LENGTH; } private: friend class CDmeGraphEditorCurve; // GraphEditorCurve is a friend so that it may manipulate the key values friend class CUndoGraphEditorModifyKeys; // The modify keys undo element is also allowed to touch keys directly to restore state // Key time and value manipulation, these functions are private as they are intended to be used // only by the CDmeGraphEditorCurve to which the key belongs. This is because the curve needs // to know when these values change, in particular the keys are stored ordered by time in the // curve, so the change of key time may require re-ordering of the key list in the curve. void SetTime( DmeTime_t time ) { m_Time = time; } void SetValue( float value ) { m_Value = value; } void SetInMode( KeyTangentMode_t mode ) { m_InMode = mode; } void SetOutMode( KeyTangentMode_t mode ) { m_OutMode = mode; } // Save all of the current values of the key internally. void StoreCurrentValues(); // Mark the previous values as being invalid void ClearPreviousValues() { m_bOldValuesValid = false; } // The old values are copied from the current values of the key by calling StoreCurrentValues(). These // are temporary values that are used to determine the delta values of operations. The following functions // return the old values if they have been stored or the current values if the old values have not been stored. bool GetPreviousStepped() const { return m_bOldValuesValid ? ( m_OldOutMode == TANGENT_MODE_STEPPED ) : ( m_OutMode == TANGENT_MODE_STEPPED ); } DmeTime_t GetPreviousTime() const { return m_bOldValuesValid ? m_OldTime : m_Time; } float GetPreviousValue() const { return m_bOldValuesValid ? m_OldValue : m_Value; } DmeTime_t GetPreviousInTime() const { return m_bOldValuesValid ? m_OldInTime : m_InTime; } float GetPreviousInDelta() const { return m_bOldValuesValid ? m_OldInDelta : m_InDelta; } DmeTime_t GetPreviousOutTime() const { return m_bOldValuesValid ? m_OldOutTime : m_OutTime; } float GetPreviousOutDelta() const { return m_bOldValuesValid ? m_OldOutDelta : m_OutDelta; } // Multi-selected tangent state, these will return true if the specified tangent is selected or just the key is selected bool InMultiSelected() const { return m_InSelected || ( m_Selected && !m_OutSelected ); } bool OutMultiSelected() const { return m_OutSelected || ( m_Selected && !m_InSelected ); } int m_Component; // Index of the component of the curve the key is associated with int m_KeyDirtyFlags; // Flags indicating if the key has been changed and on which side CDmaVar< bool > m_Selected; // "selected" : flag indicating if the key is currently selected CDmaVar< bool > m_InSelected; // "inSelected" : flag indicating if the in tangent of the key is selected CDmaVar< bool > m_OutSelected; // "outSelected" : flag indicating if the out tangent of the key is selected CDmaVar< bool > m_Weighted; // "weighted" : flag indicating if the tangents of the keys are operating in weighted mode CDmaVar< bool > m_Unified; // "unified" : flag indicating if the tangents of the key are to be manipulated together CDmaVar< int > m_InMode; // "inMode" : mode in which the in tangent is currently operating CDmaVar< int > m_OutMode; // "outMode" : mode in which the out tangent is currently operating CDmaVar< DmeTime_t > m_Time; // "time" : The time the key is located at CDmaVar< float > m_Value; // "value" : The value of the curve at the point of the key CDmaVar< DmeTime_t > m_InTime; // "inTime" : The time length of the left tangent of the key CDmaVar< float > m_InDelta; // "inDelta" : The delta value of the left tangent of the key CDmaVar< DmeTime_t > m_OutTime; // "outTime" : The time length of the right tangent of the key CDmaVar< float > m_OutDelta; // "outDelta" : The delta value of the right tangent of the key bool m_bOldValuesValid; // Flag indicating if the set of old values have been stored and are valid int m_OldInMode; // Previous in tangent operation mode int m_OldOutMode; // Previous out tangent operation mode DmeTime_t m_OldTime; // Previous time of the key float m_OldValue; // Previous value of the key DmeTime_t m_OldInTime; // Previous time length of the in tangent float m_OldInDelta; // Previous delta value of the in tangent DmeTime_t m_OldOutTime; // Previous time length of the out tangent float m_OldOutDelta; // Previous delta value of the out tangent }; //----------------------------------------------------------------------------- // The CDmeEditCurve represents a channel which is being displayed for editing // int the graph editor. //----------------------------------------------------------------------------- class CDmeGraphEditorCurve : public CDmElement { DEFINE_ELEMENT( CDmeGraphEditorCurve, CDmElement ); static const int MAX_COMPONENTS = LOG_MAX_COMPONENTS; struct SamplePoint_t { DmeTime_t time; float value; }; public: // Set the channel and components assigned to the curve and initialize the curve data. void Initialize( CDmeChannel *pChannel, DmeFramerate_t framerate, bool bFrameSnap, const DmeClipStack_t &clipstack ); // Initialize the edit log. void InitializeEditLog( DmeFramerate_t framerate, const DmeClipStack_t &clipstack ); // Make sure all results of curve editing are applied to the log void Finalize(); // Determine if the CRC of the log is the same as the last time finalize was called on the curve bool VerifyLogCRC() const; // Compute the tangents of the specified key void ComputeTangentsForKey( CDmeCurveKey *pKey, bool bStepped ) const; // Set the tangents for the key to be the specified type void SetKeyTangents( CDmeCurveKey *pKey, TangentOperation_t tangentType, bool bRespectSelection, float flUnitsPerSecond ) const; // Add keys to the curve at the specified time for the specified components void AddKeysAtTime( DmeTime_t time, LogComponents_t nComponentFlags, bool bComputeTangents, AddKeyMode_t addMode, bool bVisibleOnly ); // Remove all of keys at the specified time bool RemoveKeysAtTime( DmeTime_t time ); // Add a key to the curve at the specified time on the specified component void AddKeyAtTime( DmeTime_t time, int nComponent, bool bRecomputeNeigbors ); // Set the value of the key at the specified time or create a new key at the time with the specified value CDmeCurveKey *SetKeyAtTime( DmeTime_t time, int nComponent, float flValue ); // Set the position value of the key at the specified time or create a new key at the time with the specified value void SetKeyAtTime( DmeTime_t time, LogComponents_t nComponentFlags, const Vector &position ); // Set the position value of the key at the specified time or create a new key at the time with the specified value void SetKeyAtTime( DmeTime_t time, LogComponents_t nComponentFlags, const Quaternion &orientation ); // Set the position value of the key at the specified time or create a new key at the time with the specified value void SetKeyAtTime( DmeTime_t time, LogComponents_t nComponentFlags, const CDmAttribute *pAttr ); // Remove a key from the specified component of the curve at the specified time bool RemoveKeyAtTime( DmeTime_t time, int nComponent ); // Find the keys within the specified time range void FindKeysInTimeRange( CUtlVector< CDmeCurveKey * > &keyList, DmeTime_t startTime, DmeTime_t endTime, int nComponent ); // Offset the specified keys by the specified about of time void OffsetKeyTimes( const CUtlVector< CDmeCurveKey * > &keyList, DmeTime_t timeDelta ); // Move the specified key by the specified amount void MoveKeys( const CUtlVector< CDmeCurveKey * > &moveKeyList, DmeTime_t timeDelta, float flValueDelta, const DmeClipStack_t &localTimeClipStack, DmeFramerate_t framerate, float flValueScale, float timeScale, DmeTime_t cursorTime, bool bFrameSnap, bool bUnifiedTangents ); // Scale the specified keys about the specified void ScaleKeys( const CUtlVector< CDmeCurveKey * > &keyList, float flTimeScaleFactor, float flValueScaleFactor, DmeTime_t originTime, float flOriginValue, const DmeClipStack_t &localTimeClipStack, DmeFramerate_t framerate ); // Blend the specified keys toward the provided value using the specified blend factor void BlendKeys( const CUtlVector< CDmeCurveKey * > &keyList, Vector4D targetValue, DmAttributeType_t targetValueType, float flBlendFactor ); // Blend the specified keys toward the value at the specified time using the specified blend factor void BlendKeys( const CUtlVector< CDmeCurveKey * > &keyList, DmeTime_t targetValueTime, float flBlendFactor ); // Delete the specified keys from the curve void DeleteKeys( const CUtlVector< CDmeCurveKey * > &keyList ); // Build the keys from the log bookmarks void BuildKeysFromLog( DmeFramerate_t framerate, bool bFrameSnap, const DmeClipStack_t &graphClipStack ); // Update the edit layer for changes to the specified keys void UpdateEditLayer( DmeFramerate_t framerate, const DmeClipStack_t &graphClipStack, bool bEditLayerUndoable, bool bOffsetMode ); // Flatten the layers of the edit log, overwriting the base log layer with the active edit layer void FlattenEditLog(); // Get the range of values within the specified time range bool GetValueRangeForTime( DmeTime_t minTime, DmeTime_t maxTime, float &minValue, float &maxValue ) const; // Get the shot relative time of the specified key DmeTime_t GetKeyShotTime( int nKeyIndex, int nComponent ) const; // Get the shot relative time of the specified key DmeTime_t GetKeyShotTime( CDmeCurveKey *pKey ) const; // Get the shot relative time of the key and its tangents void GetKeyShotTimes( CDmeCurveKey *pKey, DmeTime_t &keyTime, DmeTime_t &inTime, DmeTime_t &outTime ) const; // Find the neighboring keys of the specified key int FindKeyNeighbors( const CDmeCurveKey *pKey, CDmeCurveKey *&pPreviousKey, CDmeCurveKey *&pNextKey ) const; // Build the clip stack for channel used by the the curve bool BuildClipStack( DmeClipStack_t *pClipStack, CDmeClip *pRoot, CDmeClip *pShot ) const; // Get the channels clip to which the channel of the curve belongs CDmeChannelsClip *GetChannelsClip() const; // Mark all components as not being visible void ClearComponents(); // Add the specified components to the current set of components void UpdateComponents( LogComponents_t nComponentFlags ); // Get the flag specifying which components of the log are to be displayed. LogComponents_t GetComponentFlags() const; // Get the number of components in the curve int GetNumComponents() const; // Determine if the component specified by index is visible bool IsComponentVisible( int nComponentIndex ) const; // Determine if the specified component is selected bool IsComponentSelected( int nComponentIndex ) const; // Set the selected state of the component void SetComponentSelection( LogComponents_t nComponentIndex, SelectionMode_t selectionMode ); // Clear the component selection of the curve void ClearComponentSelection(); // Get all of the keys for the components specified by the provided flags void GetKeysForComponents( CUtlVector< CDmeCurveKey * > &keyList, LogComponents_t nComponentFlags ) const; // Get the value of the specified component of the edit log at the specified time float GetEditValue( DmeTime_t time, int nComponentIndex ) const; // Return true if the specified channel is the one in use by the curve bool IsCurveUsingChannel( const CDmeChannel *pChannel ) { return m_Channel == pChannel; } // Accessors CDmeChannel *GetChannel() const { return m_Channel; } const CDmeLog *GetEditLog() const { return m_EditLog; } const CDmeCurveKey *GetKey( int nIndex, int nComponent ) const { return m_KeyList[ nComponent ][ nIndex ]; } CDmeCurveKey *GetKey( int nIndex, int nComponent ) { return m_KeyList[ nComponent ][ nIndex ]; } int GetNumKeys( int nComponent ) const { return m_KeyList[ nComponent ].Count(); } bool IsVisibleX() const { return GetValue< bool >( "xVisible", false ); } bool IsVisibleY() const { return GetValue< bool >( "yVisible", false ); } bool IsVisibleZ() const { return GetValue< bool >( "zVisible", false ); } private: // Find the key on the specified component at the specified time int FindKeyAtLocalTime( DmeTime_t localTime, int nComponent ) const; // Find the index of the specified key int FindKeyIndex( const CDmeCurveKey *pKey ) const; // Find the neighboring keys of the key with the specified index void FindKeyNeighbors( int nKeyIndex, int nComponent, CDmeCurveKey *&pPreviousKey, CDmeCurveKey *&pNextKey ) const; // Find the previous neighbors of the key using the stored key ordering list bool FindOldKeyNeighbors( const CDmeCurveKey *pKey, CDmeCurveKey *&pPreviousKey, CDmeCurveKey *&pNextKey ) const; // Find the key which was previously at the first key of the curve segment containing the specified time. const CDmeCurveKey *FindOldSegmentForTime( DmeTime_t time, int nComponent ) const; // Create a key at the specified time, with the specified value. CDmeCurveKey *CreateKey( DmeTime_t time, float flValue, int nComponent, bool bRecomputeNeighbors ); // Remove the specified key from the curve void RemoveKey( int nKeyIndex, int nComponent ); // Compute the tangents for the curve segment between the two keys void ComputeKeyTangentsForSegment( CDmeCurveKey *pStartKey, CDmeCurveKey *pEndKey, bool bAllowStepped ) const; // Update the tangent handles of keys based on the current mode void UpdateTangentHandles(); // Update the key list so that all keys are valid and in order void FixupKeyList( const CUtlVector< CDmeCurveKey * > &editKeyList ); // Make sure all of the keys are in order, but make no other changes to the keys void SortKeyList(); // Make a copy of the current key list so that it may be used to compare the key ordering after changes have been made void StoreCurrentKeyList(); // Clear the copy of the stored key list void ClearCachedKeyList(); // Update the base log from the edit log. void UpdateBaseLog( bool bUseEditLayer ); // Update the edit layer for changes to the specified keys template < typename T > void UpdateEditLayer( CDmeTypedLog< T > *pEditLog, DmeFramerate_t framerate, const DmeClipStack_t &graphClipstack, bool bEditLayerUndoable, bool bOffsetMode ); // Generate samples for the curve between two keys and write them into the edit layer template < typename T > void GenerateCurveSamples( const CDmeCurveKey *pKeyA, const CDmeCurveKey *pKeyB, CUtlVector< DmeTime_t > &sampleTimes, CUtlVector< T > &sampleValues, int nComponent, const CDmeTypedLogLayer< T > *pBaseLayer, int &nLayerKeyIndex, DmeFramerate_t sampleRate, const DmeClipStack_t &clipstack, bool bOffsetMode ) const; // Generate sample values for the specified component using the provided sample points template < typename T > void GenerateSampleValues( const CUtlVector< Vector2D > &points, const CUtlVector< DmeTime_t > &pointTimes, CUtlVector< DmeTime_t > &sampleTimes, CUtlVector< T > &sampleValues, int nComponent, const CDmeTypedLogLayer< T > *pBaseLayer, int &nLayerKeyIndex, DmeFramerate_t sampleRate, const DmeClipStack_t &clipstack ) const; // Generate sample values for the specified component using the provided previous curve points and the new curve points template < typename T > void GenerateOffsetSampleValues( const CUtlVector< Vector2D > &points, const CUtlVector< DmeTime_t > &pointTimes, const CUtlVector< Vector2D > &prevPoints, const CUtlVector< DmeTime_t > &prevPointTimes, CUtlVector< DmeTime_t > &sampleTimes, CUtlVector< T > &sampleValues, int nComponent, const CDmeTypedLogLayer< T > *pBaseLayer, int nLayerKeyIndex, DmeFramerate_t sampleRate, const DmeClipStack_t &clipstack, bool bRescaleTime ) const; // Get the range of values within the specified time range for the specified log template < typename T > bool GetValueRangeForTime( CDmeTypedLog< T > *pEditLog, DmeTime_t minTime, DmeTime_t maxTime, float &minValue, float &maxValue ) const; // Update the log bookmarks to match the keys void UpdateLogBookmarks() const; // Compute the crc of the log associated with channel CRC32_t ComputeLogCRC() const; // Re-scale the provided list of times from one time frame to another. static void RescaleTimes( DmeTime_t prevStart, DmeTime_t prevEnd, DmeTime_t newStart, DmeTime_t newEnd, const CUtlVector< DmeTime_t > &oldTimes, CUtlVector< DmeTime_t > &newTimes ); // Interleave a set of times with times occurring at a regular global interval between the provided start and end time. static void InterleaveGlobalSampleTimes( DmeTime_t startTime, DmeTime_t endTime, const CUtlVector< DmeTime_t > &existingSampleTimes, CUtlVector< DmeTime_t > &sampleTimes, DmeFramerate_t sampleRate, const DmeClipStack_t &clipstack ); private: CDmaElement< CDmeChannel > m_Channel; // "channel" : The channel associated with the curve CDmaElement< CDmeLog > m_EditLog; // "editLog" : Log that will be used for editing, for quaternions this is the log of Euler angles. CDmaElementArray< CDmeCurveKey > m_KeyList[ MAX_COMPONENTS ]; // "keyList" : List of keys on the curve CDmaVar< int > m_ComponentSelection; // "componentSelection" : Flags specifying which components are selected CRC32_t m_logCRC; // CRC value of the log the last time Finalize() was called CDmeLogLayer *m_pEditLayer; // Pointer to the log layer used to store the results of the curve manipulation CUtlVector< CDmeCurveKey * > m_OldKeyList[ MAX_COMPONENTS ]; // A list of the ordering of keys before the current modification friend class CUndoGraphEditorModifyKeys; }; // Quaternion <==> Euler conversion utilities. void QuaternionToEuler( const Quaternion &q, Vector &rotation, Vector &absRotation ); void EulerToQuaternion( const Vector &euler, Quaternion &quat ); bool ConvertLogEulerToQuaternion( const CDmeVector3LogLayer *pEulerLayer, CDmeQuaternionLogLayer *pQuatLayer ); bool ConvertLogQuaterionToEuler( const CDmeQuaternionLogLayer *pQuatLayer, CDmeVector3LogLayer *pEulerLayer, DmeFramerate_t sampleRate, const DmeClipStack_t &clipstack ); #endif // DMEGRAPHEDITORCURVE_H