You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
863 lines
26 KiB
863 lines
26 KiB
//========= 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 ); |
|
} |