//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "unitlib/unitlib.h" #include "datamodel/dmelement.h" #include "movieobjects/movieobjects.h" #include "datamodel/idatamodel.h" #include "tier1/utlbuffer.h" #include "filesystem.h" #include "movieobjects/dmelog.h" #include "choreoscene.h" #include "choreoevent.h" struct data_t { int tms; // tenths of a millisecond float value; int curvetype; }; struct tvpair_t { int tms; float expectedvalue; }; data_t data[] = { { 0, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, { 10000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, { 20000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_IN, INTERPOLATE_EASE_OUT ) }, { 30000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_INOUT ) }, { 40000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_BSPLINE ) }, { 50000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_BSPLINE, INTERPOLATE_LINEAR_INTERP ) }, { 60000, 1.0f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_KOCHANEK_BARTELS ) }, { 70000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS_EARLY ) }, { 80000, 0.5f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_EARLY, INTERPOLATE_KOCHANEK_BARTELS_LATE ) }, { 90000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_LATE, INTERPOLATE_SIMPLE_CUBIC ) }, { 100000, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_SIMPLE_CUBIC, INTERPOLATE_CATMULL_ROM ) }, { 110000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM, INTERPOLATE_CATMULL_ROM_NORMALIZE ) }, { 120000, 0.125f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZE, INTERPOLATE_EXPONENTIAL_DECAY ) }, { 130000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_HOLD ) }, { 140000, 0.0625f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_EXPONENTIAL_DECAY ) }, { 150000, 0.0f, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) }, }; #define NUM_DEF_TESTS 3 static data_t values1[] = { { -1, 0.0f, 0 }, }; static data_t values2[] = { { 5000, 0.5f, CURVE_DEFAULT }, { -1, 0.0f, 0 }, }; static data_t values3[] = { { 2500, 0.25f, CURVE_DEFAULT }, { 7500, 0.75f, CURVE_DEFAULT }, { -1, 0.0f, 0 }, }; static data_t *defaultvaluetest[ NUM_DEF_TESTS ] = { values1, values2, values3 }; #define NUM_TEST_VALUES 3 static tvpair_t expectedvalues1[NUM_TEST_VALUES] = { { 0, 0.5f }, { 5000, 0.5f }, { 10000, 0.5f }, }; static tvpair_t expectedvalues2[NUM_TEST_VALUES] = { { 0, 0.5f }, { 5000, 0.5f }, { 10000, 0.5f }, }; static tvpair_t expectedvalues3[NUM_TEST_VALUES] = { { 0, 0.25f }, { 5000, 0.5f }, { 10000, 0.75f }, }; static tvpair_t *expectedvalues[ NUM_DEF_TESTS ] = { expectedvalues1, expectedvalues2, expectedvalues3 }; void ResetLog( CDmeFloatLog *log, bool useCurveTypes, int startIndex = 0, int endIndex = -1 ) { log->ClearKeys(); CDmeCurveInfo *pCurveInfo = useCurveTypes ? log->GetOrCreateCurveInfo() : log->GetCurveInfo(); if ( useCurveTypes ) { pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); } else if ( !useCurveTypes && pCurveInfo ) { g_pDataModel->DestroyElement( pCurveInfo->GetHandle() ); log->SetCurveInfo( NULL ); } int i; int c; c = ARRAYSIZE( data ); for ( i = startIndex; i < c; ++i ) { log->SetKey( DmeTime_t( data[ i ].tms ), data[ i ].value, useCurveTypes ? data[ i ].curvetype : CURVE_DEFAULT ); if ( endIndex != -1 && i >= endIndex ) break; } } void CompareFloats( float f1, float f2, float tol, char const *fmt, ... ) { float diff = fabs( f1 - f2 ); if ( diff < tol ) return; char buf[ 256 ]; va_list argptr; va_start( argptr, fmt ); _vsnprintf( buf, sizeof( buf ) - 1, fmt, argptr ); va_end( argptr ); Msg( buf ); } DEFINE_TESTCASE_NOSUITE( DmxRunDefaultValueLogTest ) { Msg( "Running CDmeTypedLog default value (stereo channel w/ value 0.5) tests...\n" ); CDisableUndoScopeGuard sg; DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "" ); for ( int i = 0; i < NUM_DEF_TESTS; ++i ) { data_t *pdata = defaultvaluetest[ i ]; tvpair_t *pexpected = expectedvalues[ i ]; // Run each test CDmeFloatLog *log = CreateElement( "curve", fileid ); if ( !log ) { Msg( "Unable to create CDmeFloatLog object!!!" ); continue; } log->SetDefaultValue( 0.5f ); if ( pdata ) { // Run the test for ( int j = 0; ; ++j ) { if ( pdata[ j ].tms == -1 ) break; log->SetKey( DmeTime_t( pdata[ j ].tms ), pdata[ j ].value ); } } // Now compare against expected values for ( int j = 0; j < NUM_TEST_VALUES; ++j ) { DmeTime_t t = DmeTime_t( pexpected[ j ].tms ); float v = pexpected[ j ].expectedvalue; float logv = log->GetValue( t ); Shipping_Assert( v == logv ); } DestroyElement( log ); } g_pDataModel->RemoveFileId( fileid ); } void RunDmeFloatLogTests( CDmeFloatLog *log ) { Msg( " Testing general log data...\n" ); ResetLog( log, false ); CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.0f ) ), 0.000001f, "log->GetValue( 2.0 ) expected to be 0.5f\n" ); CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" ); CompareFloats( 0.5f, log->GetValue( DmeTime_t( 2.5f ) ), 0.000001f, "log->GetValue( 2.5 ) expected to be 0.5f\n" ); CompareFloats( 0.5f, log->GetValue( DmeTime_t( 6.5f ) ), 0.000001f, "log->GetValue( 6.5 ) expected to be 0.5f\n" ); CDmeCurveInfo *pCurveInfo = log->GetOrCreateCurveInfo(); int idx = log->FindKeyWithinTolerance( DmeTime_t( 6.0f ), DmeTime_t( 0 ) ); Shipping_Assert( log->GetKeyTime( idx ) == DmeTime_t( 6.0f ) ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); float val = log->GetValue( DmeTime_t( 6.5f ) ); float qval = log->GetValue( DmeTime_t( 6.25f ) ); CompareFloats( 0.5f, val, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6500 ) expcted to be 0.5f\n" ); CompareFloats( 0.75f, qval, 0.000001f, "INTERPOLATE_LINEAR_INTERPlog->GetValue( 6250 ) expcted to be 0.75f\n" ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) ); float val2 = log->GetValue( DmeTime_t( 6.5f ) ); float qval2 = log->GetValue( DmeTime_t( 6.25f ) ); Shipping_Assert( val2 == val ); Shipping_Assert( qval2 != val ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EASE_INOUT, INTERPOLATE_EASE_INOUT ) ); float val3 = log->GetValue( DmeTime_t( 6.5f ) ); float qval3 = log->GetValue( DmeTime_t( 6.25f ) ); Shipping_Assert( val3 == val ); Shipping_Assert( qval3 != val ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_EXPONENTIAL_DECAY, INTERPOLATE_EXPONENTIAL_DECAY ) ); float val4 = log->GetValue( DmeTime_t( 6.5f ) ); float qval4 = log->GetValue( DmeTime_t( 6.25f ) ); Shipping_Assert( val4 != val ); Shipping_Assert( qval4 != val ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); float val5 = log->GetValue( DmeTime_t( 6.5f ) ); float qval5 = log->GetValue( DmeTime_t( 6.25f ) ); Shipping_Assert( val5 == val ); Shipping_Assert( qval5 != val ); pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ) ); log->SetKeyCurveType( idx, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) ); log->SetKeyCurveType( idx + 1, MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ) ); float val6 = log->GetValue( DmeTime_t( 6.5f ) ); float qval6 = log->GetValue( DmeTime_t( 6.25f ) ); Shipping_Assert( val5 == val6 ); Shipping_Assert( qval6 == qval5 ); } void CompareLogToChoreo( CFlexAnimationTrack *track, CDmeFloatLog *log ) { // Now run tests for ( DmeTime_t t( 0 ); t < DmeTime_t( 20.0f ); t += DmeTime_t( 0.1f ) ) { // Compare values float dmevalue = log->GetValue( t ); float choreovalue = track->GetIntensity( t.GetSeconds() ); CompareFloats( dmevalue, choreovalue, 0.001f, "Time(%f sec) , dme [%f] choreo[%f], diff[%f]\n", t.GetSeconds(), dmevalue, choreovalue, fabs( dmevalue - choreovalue ) ); } } void ResetChoreo( CFlexAnimationTrack *track, bool useCurveTypes, int startIndex = 0, int endIndex = -1 ) { track->Clear(); int i; int c; c = ARRAYSIZE( data ); for ( i = startIndex; i < c; ++i ) { data_t *e = &data[ i ]; float t = (float)e->tms / 10000.0f; CExpressionSample *sample = track->AddSample( t, e->value ); Shipping_Assert( sample ); if ( useCurveTypes ) { sample->SetCurveType( e->curvetype ); } if ( endIndex != -1 && i >= endIndex ) break; } } void RunDmeChoreoComparisons( CDmeFloatLog *log ) { Msg( " Testing choreo-style log data...\n" ); ResetLog( log, true ); log->SetRightEdgeTime( DmeTime_t( 15.0f ) ); CChoreoScene *scene = new CChoreoScene( NULL ); CChoreoEvent *event = new CChoreoEvent( scene, CChoreoEvent::FLEXANIMATION, "test" ); event->SetStartTime( 0.0f ); event->SetEndTime( 15.0f ); CFlexAnimationTrack *track = new CFlexAnimationTrack( event ); track->SetFlexControllerName( "flextest" ); track->SetComboType( false ); ResetChoreo( track, true ); Msg( " Comparing default data...\n" ); CompareLogToChoreo( track, log ); ResetLog( log, true, 3, 14 ); ResetChoreo( track, true, 3, 14 ); Msg( " Comparing subset of data...\n" ); CompareLogToChoreo( track, log ); Msg( " Comparing left/right edge settings...\n" ); // Now test right and left edge stuff // Enable left edge stuff track->SetEdgeActive( true, true ); track->SetEdgeInfo( true, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ), 0.75f ); track->SetEdgeActive( false, true ); track->SetEdgeInfo( false, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ), 0.25f ); // Same settings for log log->SetUseEdgeInfo( true ); log->SetDefaultEdgeZeroValue( 0.0f ); log->SetEdgeInfo( 0, true, 0.75f, MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ) ); log->SetEdgeInfo( 1, true, 0.25f, MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ) ); CompareLogToChoreo( track, log ); int i; for ( i = 1; i < NUM_INTERPOLATE_TYPES; ++i ) { Msg( " Comparing left/right edge settings[ %s ]...\n", Interpolator_NameForInterpolator( i, true ) ); float val = (float)i / (float)( NUM_INTERPOLATE_TYPES - 1 ) ; // Now test right and left edge stuff with different data track->SetEdgeInfo( true, MAKE_CURVE_TYPE( i, i ), val ); track->SetEdgeInfo( false, MAKE_CURVE_TYPE( i, i ), val ); log->SetEdgeInfo( 0, true, val, MAKE_CURVE_TYPE( i, i ) ); log->SetEdgeInfo( 1, true, val, MAKE_CURVE_TYPE( i, i ) ); CompareLogToChoreo( track, log ); } delete event; delete scene; } DEFINE_TESTCASE_NOSUITE( DmxTestDmeLog ) { Msg( "Running CDmeTypedLog tests...\n" ); CDisableUndoScopeGuard sg; DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( "" ); CDmeFloatLog *pElement = CreateElement( "curve", fileid ); if ( !pElement ) { Msg( "Unable to create CDmeFloatLog object!!!" ); return; } // Run tests RunDmeFloatLogTests( pElement ); RunDmeChoreoComparisons( pElement ); g_pDataModel->RemoveFileId( fileid ); }