summaryrefslogtreecommitdiff
path: root/movieobjects/importintovcd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'movieobjects/importintovcd.cpp')
-rw-r--r--movieobjects/importintovcd.cpp863
1 files changed, 863 insertions, 0 deletions
diff --git a/movieobjects/importintovcd.cpp b/movieobjects/importintovcd.cpp
new file mode 100644
index 0000000..9b88605
--- /dev/null
+++ b/movieobjects/importintovcd.cpp
@@ -0,0 +1,863 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/importintovcd.h"
+#include "movieobjects/movieobjects.h"
+#include "tier3/scenetokenprocessor.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoevent.h"
+#include "tier2/p4helpers.h"
+#include "tier1/utlbuffer.h"
+#include "tier3/tier3.h"
+#include "datacache/imdlcache.h"
+#include "filesystem.h"
+#include "studio.h"
+
+
+//-----------------------------------------------------------------------------
+// Helper wrapper class for log layers (necessary to avoid movieobjects dependence)
+//-----------------------------------------------------------------------------
+class CDmeLogLayerHelper
+{
+public:
+ CDmeLogLayerHelper( CDmElement *pLogLayer, int nDefaultCurveType );
+
+ // Finds a key
+ int FindKey( int nTime ) const;
+
+ // Gets a value at a particular time
+ float GetValue( int nTime ) const;
+
+ // Inserts keys
+ void AddToTail( int nTime, float flValue, int nCurveType );
+ void InsertAfter( int nAfter, int nTime, float flValue, int nCurveType );
+ int InsertKey( int nTime, float flValue, int nCurveType );
+
+ // Simplifies the curve
+ void Simplify( float flThreshhold );
+
+ void SetCurveType( int nKey, int nCurveType );
+
+ // Total simplified points
+ static int TotalRemovedPoints();
+
+private:
+ void CurveSimplify_R( float flThreshold, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest );
+
+ // Computes the total error
+ float ComputeTotalError( CDmeLogLayerHelper *pDest, int nStartPoint, int nEndPoint );
+
+ // Select the best fit curve type
+ void ChooseBestCurveType( int nKey, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest );
+
+ // Compute first + second derivatives of data
+ void ComputeDerivates( float *pSlope, float *pAccel, int nPoint, CDmeLogLayerHelper *pDest );
+
+ CDmElement *m_pLogLayer;
+ CDmrArray<int> m_times;
+ CDmrArray<float> m_values;
+ CDmrArray<int> m_curvetypes;
+ int m_nDefaultCurveType;
+
+ static int s_nTotalRemovedPoints;
+};
+
+
+//-----------------------------------------------------------------------------
+// Total simplified points
+//-----------------------------------------------------------------------------
+int CDmeLogLayerHelper::s_nTotalRemovedPoints = 0;
+int CDmeLogLayerHelper::TotalRemovedPoints()
+{
+ return s_nTotalRemovedPoints;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmeLogLayerHelper::CDmeLogLayerHelper( CDmElement *pLogLayer, int nDefaultCurveType ) :
+ m_pLogLayer( pLogLayer ), m_times( pLogLayer, "times", true ),
+ m_values( pLogLayer, "values", true ), m_curvetypes( pLogLayer, "curvetypes", true )
+{
+ m_nDefaultCurveType = nDefaultCurveType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts keys
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::AddToTail( int nTime, float flValue, int nCurveType )
+{
+ m_times.AddToTail( nTime );
+ m_values.AddToTail( flValue );
+ m_curvetypes.AddToTail( nCurveType );
+}
+
+void CDmeLogLayerHelper::InsertAfter( int nAfter, int nTime, float flValue, int nCurveType )
+{
+ int nBefore = nAfter + 1;
+ m_times.InsertBefore( nBefore, nTime );
+ m_values.InsertBefore( nBefore, flValue );
+ m_curvetypes.InsertBefore( nBefore, nCurveType );
+}
+
+int CDmeLogLayerHelper::InsertKey( int nTime, float flValue, int nCurveType )
+{
+ int nAfter = FindKey( nTime );
+ InsertAfter( nAfter, nTime, flValue, nCurveType );
+ return nAfter + 1;
+}
+
+void CDmeLogLayerHelper::SetCurveType( int nKey, int nCurveType )
+{
+ m_curvetypes.Set( nKey, nCurveType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a key
+//-----------------------------------------------------------------------------
+int CDmeLogLayerHelper::FindKey( int nTime ) const
+{
+ int tn = m_times.Count();
+ for ( int ti = tn - 1; ti >= 0; --ti )
+ {
+ if ( nTime >= m_times[ ti ] )
+ return ti;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a value at a particular time
+//-----------------------------------------------------------------------------
+float CDmeLogLayerHelper::GetValue( int nTime ) const
+{
+ int tc = m_times.Count();
+
+ Assert( m_values.Count() == tc );
+
+ int ti = FindKey( nTime );
+ if ( ti < 0 )
+ {
+ if ( tc > 0 )
+ return m_values[ 0 ];
+ return 0.0f;
+ }
+
+ // Early out if we're at the end
+ if ( ti >= tc - 1 )
+ return m_values[ ti ];
+
+ // Figure out the lerp factor
+ int nDummy, nInterpolationType;
+ int nCurveType = m_curvetypes.Count() ? m_curvetypes[ti] : m_nDefaultCurveType;
+ Interpolator_CurveInterpolatorsForType( nCurveType, nInterpolationType, nDummy );
+
+ Vector vecOutput;
+ Vector vecArg1( 0.0f, m_values[ti], 0.0f );
+ Vector vecArg2( 1.0f, m_values[ti+1], 0.0f );
+ float t = (float)( nTime - m_times[ti] ) / (float)( m_times[ti+1] - m_times[ti] );
+ Interpolator_CurveInterpolate( nInterpolationType, vecArg1, vecArg1, vecArg2, vecArg2, t, vecOutput );
+ return vecOutput.y;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the total error
+//-----------------------------------------------------------------------------
+float CDmeLogLayerHelper::ComputeTotalError( CDmeLogLayerHelper *pDest, int nStartPoint, int nEndPoint )
+{
+ float flTotalDistance = 0.0f;
+
+ for ( int i = nStartPoint; i <= nEndPoint; ++i )
+ {
+ float flCheck = m_values[i];
+ float flCheck2 = pDest->GetValue( m_times[i] );
+ float flDistance = fabs( flCheck2 - flCheck );
+ flTotalDistance += flDistance;
+ }
+
+ return flTotalDistance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Select the best fit curve type
+//-----------------------------------------------------------------------------
+static int s_nInterpTypes[] =
+{
+ INTERPOLATE_LINEAR_INTERP,
+ INTERPOLATE_EASE_INOUT,
+// INTERPOLATE_EASE_IN,
+// INTERPOLATE_EASE_OUT,
+// INTERPOLATE_EXPONENTIAL_DECAY,
+// INTERPOLATE_HOLD,
+ -1,
+};
+
+void CDmeLogLayerHelper::ChooseBestCurveType( int nKey, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest )
+{
+ return;
+
+ float flMinError = FLT_MAX;
+ int nBestInterpType = -1;
+ for ( int i = 0; s_nInterpTypes[i] >= 0; ++i )
+ {
+ pDest->SetCurveType( nKey, MAKE_CURVE_TYPE( s_nInterpTypes[i], s_nInterpTypes[i] ) );
+ float flError = ComputeTotalError( pDest, nStartPoint, nEndPoint );
+ if ( flMinError > flError )
+ {
+ nBestInterpType = s_nInterpTypes[i];
+ flMinError = flError;
+ }
+ }
+ Assert( nBestInterpType >= 0 );
+ pDest->SetCurveType( nKey, MAKE_CURVE_TYPE( nBestInterpType, nBestInterpType ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute first + second derivatives of data
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::ComputeDerivates( float *pSlope, float *pAccel, int nPoint, CDmeLogLayerHelper *pDest )
+{
+ // Central difference, assume linear slope between points.
+ // Find neighboring point with minimum distance
+ bool bLeftEdge = ( nPoint == 0 );
+ bool bRightEdge = ( nPoint == m_times.Count() - 1 );
+
+ int nTime = m_times[nPoint];
+ int nPrevTime = ( !bLeftEdge ) ? m_times[ nPoint - 1 ] : nTime - 1000;
+ int nNextTime = ( !bRightEdge ) ? m_times[ nPoint + 1 ] : nTime + 1000;
+ float flPrevPoint, flNextPoint;
+ if ( nTime - nPrevTime < nNextTime - nTime )
+ {
+ // prev point is closer
+ flPrevPoint = ( !bLeftEdge ) ? m_values[ nPoint - 1 ] : m_values[ nPoint ];
+ nNextTime = nTime + ( nTime - nPrevTime );
+ flNextPoint = GetValue( nNextTime );
+ }
+ else
+ {
+ // next point is closer
+ flNextPoint = ( !bRightEdge ) ? m_values[ nPoint + 1 ] : m_values[ nPoint ];
+ nPrevTime = nTime - ( nNextTime - nTime );
+ flPrevPoint = GetValue( nPrevTime );
+ }
+
+ // Central difference: slope = ( vnext - vprev ) / ( tnext - tprev );
+ // accel = ( vnext - 2 * vcurr + vprev ) / ( 0.5 * ( tnext - tprev ) )^2
+ float flCurrPoint = m_values[nPoint];
+ flPrevPoint -= pDest->GetValue( nPrevTime );
+ flCurrPoint -= pDest->GetValue( nTime );
+ flNextPoint -= pDest->GetValue( nNextTime );
+
+ float flDeltaTime = DMETIME_TO_SECONDS( nTime - nPrevTime );
+ *pSlope = ( flNextPoint - flPrevPoint ) / ( 2.0f * flDeltaTime );
+ *pAccel = ( flNextPoint - 2 * flCurrPoint + flPrevPoint ) / ( flDeltaTime * flDeltaTime );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Implementation of Douglas-Peucker curve simplification routine
+// (hacked to only care about error against original curve (sort of 1D)
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::CurveSimplify_R( float flThreshold, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest )
+{
+ if ( nEndPoint <= nStartPoint + 1 )
+ return;
+
+ int nMaxPoint = nStartPoint;
+ float flMaxDistance = 0.0f;
+
+ for ( int i = nStartPoint + 1 ; i < nEndPoint; ++i )
+ {
+ float flCheck = m_values[i];
+ float flCheck2 = pDest->GetValue( m_times[i] );
+ float flDistance = fabs( flCheck2 - flCheck );
+
+ if ( flDistance < flMaxDistance )
+ continue;
+
+ nMaxPoint = i;
+ flMaxDistance = flDistance;
+ }
+
+ /*
+ float flMaxAccel = 0.0f;
+ for ( int i = nStartPoint + 1 ; i < nEndPoint; ++i )
+ {
+ float flSlope, flAccel;
+ ComputeDerivates( &flSlope, &flAccel, i, pDest );
+ flAccel = fabs( flAccel );
+ if ( flAccel < flMaxAccel )
+ continue;
+
+ nMaxPoint = i;
+ flMaxAccel = flAccel;
+ }
+ */
+
+ if ( flMaxDistance > flThreshold )
+ {
+ int nKey = pDest->InsertKey( m_times[ nMaxPoint ], m_values[ nMaxPoint ], m_nDefaultCurveType );
+ Assert( nKey != 0 );
+ ChooseBestCurveType( nKey-1, nStartPoint, nMaxPoint, pDest );
+ ChooseBestCurveType( nKey, nMaxPoint, nEndPoint, pDest );
+
+ CurveSimplify_R( flThreshold, nStartPoint, nMaxPoint, pDest );
+ CurveSimplify_R( flThreshold, nMaxPoint, nEndPoint, pDest );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Simplifies the curve
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::Simplify( float flThreshhold )
+{
+ int nFirstKey, nLastKey;
+ int nKeys = m_values.Count();
+ if ( nKeys <= 1 )
+ return;
+
+ for ( nFirstKey = 1; nFirstKey < nKeys; ++nFirstKey )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( m_values[ nFirstKey ] != m_values[ nFirstKey - 1 ] )
+ break;
+ }
+ --nFirstKey;
+
+ for ( nLastKey = nKeys; --nLastKey >= 1; )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( m_values[ nLastKey ] != m_values[ nLastKey - 1 ] )
+ break;
+ }
+
+ if ( nLastKey <= nFirstKey )
+ {
+ m_times.RemoveMultiple( 1, nKeys - 1 );
+ m_values.RemoveMultiple( 1, nKeys - 1 );
+ s_nTotalRemovedPoints += nKeys - 1;
+ return;
+ }
+
+ CDmElement *pTemp = CreateElement< CDmElement >( "simplified" );
+ CDmeLogLayerHelper destLayer( pTemp, m_nDefaultCurveType );
+
+ destLayer.AddToTail( m_times[nFirstKey], m_values[nFirstKey], m_nDefaultCurveType );
+ destLayer.AddToTail( m_times[nLastKey], m_values[nLastKey], m_nDefaultCurveType );
+
+ // Recursively finds the point with the largest error from the "simplified curve"
+ // and subdivides the problem on both sides until the largest delta from the simplified
+ // curve is less than the tolerance
+ CurveSimplify_R( flThreshhold, nFirstKey, nLastKey, &destLayer );
+
+ m_times.CopyArray( destLayer.m_times.Base(), destLayer.m_times.Count() );
+ m_values.CopyArray( destLayer.m_values.Base(), destLayer.m_values.Count() );
+ m_curvetypes.CopyArray( destLayer.m_curvetypes.Base(), destLayer.m_curvetypes.Count() );
+
+ DestroyElement( pTemp );
+
+ s_nTotalRemovedPoints += nKeys - m_times.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or adds actors, channels
+//-----------------------------------------------------------------------------
+static CChoreoActor* FindOrAddActor( CChoreoScene *pScene, const char *pActorName, const char *pActorModel )
+{
+ CChoreoActor *a = pScene->FindActor( pActorName );
+ if ( !a )
+ {
+ a = pScene->AllocActor();
+ Assert( a );
+ a->SetName( pActorName );
+ a->SetActive( true );
+ a->SetFacePoserModelName( pActorModel );
+ }
+ return a;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds animation events
+//-----------------------------------------------------------------------------
+static CChoreoEvent* FindOrAddAnimationEvent( CChoreoScene *pScene, CChoreoActor *pActor )
+{
+ int nEventCount = pScene->GetNumEvents();
+ for ( int i = 0; i < nEventCount; ++i )
+ {
+ CChoreoEvent* pEvent = pScene->GetEvent(i);
+ if ( pEvent->GetActor() != pActor )
+ continue;
+
+ if ( pEvent->GetType() != CChoreoEvent::FLEXANIMATION )
+ continue;
+
+ return pEvent;
+ }
+
+ // Allocate new channel
+ CChoreoChannel *pChannel = pScene->AllocChannel();
+ pChannel->SetName( "imported_flex" );
+ pChannel->SetActor( pActor );
+ pChannel->SetActive( true );
+ pActor->AddChannel( pChannel );
+
+ // Allocate choreo event
+ CChoreoEvent *pEvent = pScene->AllocEvent();
+ pEvent->SetName( pActor->GetName() );
+ pEvent->SetType( CChoreoEvent::FLEXANIMATION );
+ pEvent->SetActor( pActor );
+ pEvent->SetChannel( pChannel );
+ pEvent->SetActive( true );
+ pChannel->AddEvent( pEvent );
+
+ return pEvent;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds sound events
+//-----------------------------------------------------------------------------
+static CChoreoEvent* FindOrAddSoundEvent( CChoreoScene *pScene, CChoreoActor *pActor, const char *pEventName )
+{
+ int nEventCount = pScene->GetNumEvents();
+ for ( int i = 0; i < nEventCount; ++i )
+ {
+ CChoreoEvent* pEvent = pScene->GetEvent(i);
+ if ( pEvent->GetActor() != pActor )
+ continue;
+
+ if ( pEvent->GetType() != CChoreoEvent::SPEAK )
+ continue;
+
+ if ( Q_stricmp( pEvent->GetName(), pEventName ) )
+ continue;
+
+ return pEvent;
+ }
+
+ // Allocate new channel
+ CChoreoChannel *pChannel = pScene->AllocChannel();
+ pChannel->SetName( "imported sounds" );
+ pChannel->SetActor( pActor );
+ pChannel->SetActive( true );
+ pActor->AddChannel( pChannel );
+
+ // Allocate sound event
+ CChoreoEvent *pEvent = pScene->AllocEvent();
+ pEvent->SetName( pEventName );
+ pEvent->SetType( CChoreoEvent::SPEAK );
+ pEvent->SetActor( pActor );
+ pEvent->SetChannel( pChannel );
+ pEvent->SetActive( true );
+ pChannel->AddEvent( pEvent );
+
+ return pEvent;
+}
+
+
+static CFlexAnimationTrack *FindOrCreateTrack( CChoreoEvent *pEvent, const char *pFlexControllerName )
+{
+ CFlexAnimationTrack *pTrack = pEvent->FindTrack( pFlexControllerName );
+ if ( pTrack )
+ {
+ pTrack->Clear();
+ }
+ else
+ {
+ pTrack = pEvent->AddTrack( pFlexControllerName );
+ pTrack->SetTrackActive( true );
+ }
+
+ pTrack->SetMin( 0.0f );
+ pTrack->SetMax( 1.0f );
+ pTrack->SetInverted( false );
+
+ return pTrack;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns flex controller ranges
+//-----------------------------------------------------------------------------
+void GetStereoFlexControllerRange( float *pMin, float *pMax, studiohdr_t *pStudioHdr, const char *pFlexName )
+{
+ char pRightBuf[MAX_PATH];
+ char pLeftBuf[MAX_PATH];
+ Q_snprintf( pRightBuf, sizeof(pRightBuf), "right_%s", pFlexName );
+ Q_snprintf( pLeftBuf, sizeof(pLeftBuf), "left_%s", pFlexName );
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < pStudioHdr->numflexcontrollers; ++i )
+ {
+ mstudioflexcontroller_t *pFlex = pStudioHdr->pFlexcontroller( i );
+ const char *pFlexControllerName = pFlex->pszName();
+ if ( !Q_stricmp( pFlexControllerName, pFlexName ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+
+ // FIXME: Probably want to get the left + right controller + find the min and max of each, but this is unnecessary.
+ if ( !Q_stricmp( pFlexControllerName, pRightBuf ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+
+ }
+ *pMin = 0.0f;
+ *pMax = 1.0f;
+}
+
+
+void GetFlexControllerRange( float *pMin, float *pMax, studiohdr_t *pStudioHdr, const char *pFlexName )
+{
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < pStudioHdr->numflexcontrollers; ++i )
+ {
+ mstudioflexcontroller_t *pFlex = pStudioHdr->pFlexcontroller( i );
+ const char *pFlexControllerName = pFlex->pszName();
+ if ( !Q_stricmp( pFlexControllerName, pFlexName ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+ }
+ *pMin = 0.0f;
+ *pMax = 1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports samples into a track
+//-----------------------------------------------------------------------------
+void ImportSamplesIntoTrack( CFlexAnimationTrack *pTrack, CDmElement *pLog, int nSampleType, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> times( pLog, "times" );
+ CDmrArray<float> values( pLog, "values" );
+
+ // Add the samples
+ int nSampleCount = times.Count();
+ if ( nSampleCount == 0 )
+ return;
+
+ int nDefaultCurveType = MAKE_CURVE_TYPE( info.m_nInterpolationType, info.m_nInterpolationType );
+ if ( info.m_flSimplificationThreshhold > 0.0f )
+ {
+ CDmeLogLayerHelper helper( pLog, nDefaultCurveType );
+ helper.Simplify( info.m_flSimplificationThreshhold );
+ }
+
+ CDmrArray<int> curveTypes( pLog, "curvetypes" );
+
+ nSampleCount = times.Count();
+ bool bHasCurveTypeData = ( curveTypes.Count() > 0 );
+ for ( int j = 0; j < nSampleCount; ++j )
+ {
+ int nCurveType = bHasCurveTypeData ? curveTypes[j] : nDefaultCurveType;
+ float flValue = values[j];
+ float flTime = DMETIME_TO_SECONDS( times[j] - nTimeOffset );
+
+ CExpressionSample *pSample = pTrack->AddSample( flTime, flValue, nSampleType );
+ pSample->SetCurveType( nCurveType );
+ }
+
+ if ( nSampleType == 0 )
+ {
+ pTrack->SetEdgeActive( true, true );
+ pTrack->SetEdgeActive( false, true );
+
+ int nCurveType0, nCurveType1;
+ if ( bHasCurveTypeData )
+ {
+ nCurveType0 = curveTypes[0];
+ nCurveType1 = curveTypes[nSampleCount-1];
+ }
+ else
+ {
+ nCurveType0 = nCurveType1 = nDefaultCurveType;
+ }
+ pTrack->SetEdgeInfo( true, nCurveType0, values[ 0 ] );
+ pTrack->SetEdgeInfo( false, nCurveType1, values[ nSampleCount-1 ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports mono log data into a event, creates a new track if necessary
+//-----------------------------------------------------------------------------
+void ImportMonoLogDataIntoEvent( studiohdr_t *pStudioHdr, CChoreoEvent *pEvent, const char *pTrackName, CDmElement *pLog, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> times( pLog, "times" );
+ if ( times.Count() == 0 )
+ return;
+
+ float flMin, flMax;
+ GetFlexControllerRange( &flMin, &flMax, pStudioHdr, pTrackName );
+
+ CFlexAnimationTrack *pTrack = FindOrCreateTrack( pEvent, pTrackName );
+ pTrack->Clear();
+ pTrack->SetComboType( false );
+ pTrack->SetMin( flMin );
+ pTrack->SetMax( flMax );
+ ImportSamplesIntoTrack( pTrack, pLog, 0, nTimeOffset, info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports stereo log data into a event, creates a new track if necessary
+//-----------------------------------------------------------------------------
+void ImportStereoLogDataIntoEvent( studiohdr_t *pStudioHdr, CChoreoEvent *pEvent, const char *pTrackName, CDmElement *pValueLog, CDmElement *pBalanceLog, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> valueTimes( pValueLog, "times" );
+ CDmrArray<int> balanceTimes( pBalanceLog, "times" );
+ if ( valueTimes.Count() == 0 && balanceTimes.Count() == 0 )
+ return;
+
+ float flMin, flMax;
+ GetStereoFlexControllerRange( &flMin, &flMax, pStudioHdr, pTrackName );
+
+ CFlexAnimationTrack *pTrack = FindOrCreateTrack( pEvent, pTrackName );
+ pTrack->Clear();
+ pTrack->SetComboType( true );
+ pTrack->SetMin( flMin );
+ pTrack->SetMax( flMax );
+ ImportSamplesIntoTrack( pTrack, pValueLog, 0, nTimeOffset, info );
+ ImportSamplesIntoTrack( pTrack, pBalanceLog, 1, nTimeOffset, info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute track start, end time
+//-----------------------------------------------------------------------------
+static int ComputeEventTime( CDmElement *pRoot, CChoreoEvent *pEvent )
+{
+ int nStartTime = INT_MAX;
+ int nEndTime = INT_MIN;
+
+ // Iterate over all elements in the animations attribute; each one refers to a log.
+ CDmrElementArray<> animations( pRoot, "animations" );
+ if ( !animations.IsValid() )
+ return 0;
+
+ int nCount = animations.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pLog = animations[i];
+ if ( !pLog )
+ continue;
+
+ CDmrArray<int> times( pLog, "times" );
+ int nSampleCount = times.Count();
+ if ( nSampleCount == 0 )
+ continue;
+
+ if ( nStartTime > times[0] )
+ {
+ nStartTime = times[0];
+ }
+ if ( nEndTime < times[nSampleCount-1] )
+ {
+ nEndTime = times[nSampleCount-1];
+ }
+ }
+
+ pEvent->SetStartTime( DMETIME_TO_SECONDS( nStartTime ) );
+ pEvent->SetEndTime( DMETIME_TO_SECONDS( nEndTime ) );
+ return nStartTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing animations
+//-----------------------------------------------------------------------------
+void ImportAnimations( CDmElement *pRoot, CChoreoScene *pChoreoScene, CChoreoActor *pActor, studiohdr_t *pStudioHdr, const ImportVCDInfo_t& info )
+{
+ CChoreoEvent *pEvent = FindOrAddAnimationEvent( pChoreoScene, pActor );
+ pEvent->SetDefaultCurveType( MAKE_CURVE_TYPE( info.m_nInterpolationType, info.m_nInterpolationType ) );
+ int nTimeOffset = ComputeEventTime( pRoot, pEvent );
+
+ // Iterate over all elements in the animations attribute; each one refers to a log.
+ CDmrElementArray<> animations( pRoot, "animations" );
+ if ( !animations.IsValid() )
+ return;
+ int nCount = animations.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pLog = animations[i];
+ if ( !pLog )
+ continue;
+
+ const char *pLogName = pLog->GetName();
+
+ // Balance is done at the same time as value
+ if ( StringHasPrefix( pLogName, "balance_" ) )
+ continue;
+
+ if ( StringHasPrefix( pLogName, "value_" ) )
+ {
+ if ( i == nCount - 1 )
+ continue;
+
+ char pBalanceName[256];
+ Q_snprintf( pBalanceName, sizeof(pBalanceName), "balance_%s", pLogName + 6 );
+ CDmElement *pBalanceLog = animations[i+1];
+ if ( !Q_stricmp( pBalanceName, pBalanceLog->GetName() ) )
+ {
+ ++i;
+ }
+ else
+ {
+ pBalanceLog = NULL;
+ }
+ if ( pBalanceLog )
+ {
+ ImportStereoLogDataIntoEvent( pStudioHdr, pEvent, pLogName + 6, pLog, pBalanceLog, nTimeOffset, info );
+ }
+ }
+ else
+ {
+ ImportMonoLogDataIntoEvent( pStudioHdr, pEvent, pLogName, pLog, nTimeOffset, info );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing sounds
+//-----------------------------------------------------------------------------
+void ImportSounds( CDmElement *pRoot, CChoreoScene *pChoreoScene, CChoreoActor *pActor, const ImportVCDInfo_t& info )
+{
+ // Iterate over all element in the sound attribute; each one refers to a sound
+ CDmrElementArray<> sounds( pRoot, "sounds" );
+ if ( !sounds.IsValid() )
+ return;
+ int nCount = sounds.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pSound = sounds[i];
+ if ( !pSound )
+ continue;
+
+ const char *pEventName = pSound->GetName();
+ CChoreoEvent *pEvent = FindOrAddSoundEvent( pChoreoScene, pActor, pEventName );
+
+ int nStart = pSound->GetValue<int>( "start" );
+ int nEnd = pSound->GetValue<int>( "end" );
+ const char *pGameSound = pSound->GetValueString( "gamesound" );
+ pEvent->SetStartTime( DMETIME_TO_SECONDS( nStart ) );
+ pEvent->SetEndTime( DMETIME_TO_SECONDS( nEnd ) );
+ pEvent->SetParameters( pGameSound );
+ pEvent->SetCloseCaptionType( CChoreoEvent::CC_MASTER );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing a .fac file into a .vcd file
+//-----------------------------------------------------------------------------
+bool ImportLogsIntoVCD( const char *pFacFullPath, CChoreoScene *pChoreoScene, const ImportVCDInfo_t& info )
+{
+ CDmElement *pRoot;
+ DmFileId_t id = g_pDataModel->RestoreFromFile( pFacFullPath, NULL, NULL, &pRoot, CR_FORCE_COPY );
+ if ( id == DMFILEID_INVALID )
+ {
+ Warning( "Unable to load file %s\n", pFacFullPath );
+ return false;
+ }
+
+ pChoreoScene->IgnorePhonemes( info.m_bIgnorePhonemes );
+
+ // Create the actor in the scene
+ const char *pActorName = pRoot->GetName();
+ const char *pActorModel = pRoot->GetValueString( "gamemodel" );
+
+ MDLHandle_t hMDL = g_pMDLCache->FindMDL( pActorModel );
+ if ( hMDL == MDLHANDLE_INVALID )
+ {
+ Warning( "vcdimport: Model %s doesn't exist!\n", pActorModel );
+ return false;
+ }
+
+ studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL );
+ if ( !pStudioHdr || g_pMDLCache->IsErrorModel( hMDL ) )
+ {
+ Warning( "vcdimport: Model %s doesn't exist!\n", pActorModel );
+ return false;
+ }
+
+ CChoreoActor *pActor = FindOrAddActor( pChoreoScene, pActorName, pActorModel );
+
+ ImportAnimations( pRoot, pChoreoScene, pActor, pStudioHdr, info );
+ ImportSounds( pRoot, pChoreoScene, pActor, info );
+
+ DestroyElement( pRoot, TD_DEEP );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing a .fac file into a .vcd file
+//-----------------------------------------------------------------------------
+bool ImportLogsIntoVCD( const char *pFacFullPath, const char *pVCDInFullPath, const char *pVCDOutPath, const ImportVCDInfo_t& info )
+{
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pVCDInFullPath, NULL, buf ) )
+ {
+ Warning( "Unable to load file %s\n", pVCDInFullPath );
+ return false;
+ }
+
+ SetTokenProcessorBuffer( (char *)buf.Base() );
+ CChoreoScene *pScene = ChoreoLoadScene( pVCDInFullPath, NULL, GetTokenProcessor(), NULL );
+ if ( !pScene )
+ {
+ Warning( "Unable to parse file %s\n", pVCDInFullPath );
+ return false;
+ }
+
+ bool bOk = ImportLogsIntoVCD( pFacFullPath, pScene, info );
+ if ( !bOk )
+ return false;
+
+ Msg( "Removed %d samples\n", CDmeLogLayerHelper::TotalRemovedPoints() );
+
+ char pTemp[MAX_PATH];
+ if ( !Q_IsAbsolutePath( pVCDOutPath ) )
+ {
+ g_pFullFileSystem->RelativePathToFullPath( pVCDOutPath, NULL, pTemp, sizeof(pTemp) );
+ if ( !Q_IsAbsolutePath( pTemp ) )
+ {
+ char pDir[MAX_PATH];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof(pDir) ) )
+ {
+ Q_ComposeFileName( pDir, pVCDOutPath, pTemp, sizeof(pTemp) );
+ pVCDOutPath = pTemp;
+ }
+ }
+ else
+ {
+ pVCDOutPath = pTemp;
+ }
+ }
+
+ CP4AutoEditFile checkout( pVCDOutPath );
+ return pScene->SaveToFile( pVCDOutPath );
+} \ No newline at end of file