home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / LipSync.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  11KB  |  466 lines

  1. #include "../idlib/precompiled.h"
  2. #pragma hdrstop
  3.  
  4. #include "Game_local.h"
  5.  
  6. // 1    normal
  7. // 2    scared
  8. // 3    surprised
  9. // 4    panicked
  10. // 5    angry
  11. // 6    suspicious with rt eyelid raised
  12. // 7    suspicious with lft eyelid raised
  13. // 8    curious
  14. // 9    tired
  15. // 10    happy
  16.  
  17. idCVar fas_debug( "fas_debug", "0", CVAR_INTEGER, "debug info for facial animation system" );
  18. idCVar fas_threshhold0( "fas_threshhold0", "60", CVAR_INTEGER, "intensity required to use frame set 0" );
  19. idCVar fas_threshhold1( "fas_threshhold1", "30", CVAR_INTEGER, "intensity required to use frame set 1" );
  20. idCVar fas_blendBias( "fas_blendBias", "1.5", 0, "multiplier to the per phoneme blend time" );
  21. idCVar fas_intensityBias( "fas_intensityBias", "0", CVAR_INTEGER, "bias applied to the intensity of the phoneme when trying to extract the viseme" );
  22. idCVar fas_timeOffset( "fas_timeOffset", "50", CVAR_INTEGER, "ms offset to the viseme frame" );
  23.  
  24. idStr                    phonemeFile;
  25. idHashTable<rvViseme>    *visemeTable100;
  26. idHashTable<rvViseme>    *visemeTable66;
  27. idHashTable<rvViseme>    *visemeTable33;
  28.  
  29. /*
  30. ================
  31. rvViseme::Init
  32. ================
  33. */
  34. void rvViseme::Init( idStr &phon, int f, int bt )
  35. {
  36.     phoneme = phon;
  37.     frame = f;
  38.     blendTime = bt;
  39. }
  40.  
  41. /*
  42. ================
  43. FAS_LoadPhonemes
  44.  
  45. Load in the the file that cross references phonemes with visemes.
  46. ================
  47. */
  48. bool FAS_LoadPhonemes( const char *visemes )
  49. {
  50.     idStr        visemeFile;
  51.     rvViseme    viseme;
  52.     idLexer        lexer;
  53.     idToken        token;
  54.     idStr        phoneme;
  55.     int            frame, blendTime, intensity;
  56.  
  57.     phonemeFile = visemes;
  58.     visemeTable100->Clear();
  59.     visemeTable66->Clear();
  60.     visemeTable33->Clear();
  61.  
  62.     common->Printf( "Loading viseme file: %s\n", visemes );
  63.  
  64.     visemeFile = "lipsync/";
  65.     visemeFile += visemes;
  66.     visemeFile += ".viseme";
  67.  
  68.     lexer.SetFlags( DECL_LEXER_FLAGS );
  69.     lexer.LoadFile( visemeFile );
  70.  
  71.     if( !lexer.ExpectTokenString( "visemes" ) )
  72.     {
  73.         return( false );
  74.     }
  75.  
  76.     if( !lexer.ExpectTokenString( "{" ) )
  77.     {
  78.         return( false );
  79.     }
  80.  
  81.     while( true )
  82.     {
  83.         if( !lexer.ReadToken( &token ) )
  84.         {
  85.             return( false );
  86.         }
  87.  
  88.         if( token == "}" )
  89.         {
  90.             break;
  91.         }
  92.  
  93.         phoneme = token;
  94.         lexer.ExpectTokenString( "," );
  95.         frame = lexer.ParseInt();
  96.         lexer.ExpectTokenString( "," );
  97.         blendTime = lexer.ParseInt();
  98.         lexer.ExpectTokenString( "," );
  99.         intensity = lexer.ParseInt();
  100.  
  101.         viseme.Init( phoneme, frame, blendTime );
  102.  
  103.         if( intensity > fas_threshhold0.GetInteger() )
  104.         {
  105.             visemeTable100->Set( phoneme, viseme );
  106.         }
  107.         else if( intensity > fas_threshhold1.GetInteger() )
  108.         {
  109.             visemeTable66->Set( phoneme, viseme );
  110.         }
  111.         else
  112.         {
  113.             visemeTable33->Set( phoneme, viseme );
  114.         }
  115.     }
  116.  
  117.     return( true );
  118. }
  119.  
  120. /*
  121. ================
  122. rvLipSyncData
  123. ================
  124. */
  125. rvLipSyncData::rvLipSyncData( const rvDeclLipSync *ls, int time ) 
  126. {
  127.     const char *lsd = ls->GetLipSyncData();
  128.  
  129.     mLexer.SetFlags( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWNUMBERNAMES );
  130.     mLexer.LoadMemory( lsd, idStr::Length( lsd ), ls->GetName() ); 
  131.     mFlags = 0;
  132.     mFrame = 0;
  133.     mBlendTime = 0;
  134.     mEmotion = "idle";
  135.     mNextTokenTime = time;
  136. }
  137.  
  138. void rvLipSyncData::SetFrame( int frame )
  139. {
  140.     mLastFrame = mFrame;
  141.     mFrame = frame;
  142.     mVisemeStartTime = mNextTokenTime;
  143. }
  144.  
  145. float rvLipSyncData::GetFrontLerp( void )
  146. {
  147.     float lerp = 1.0f - idMath::ClampFloat( 0.0f, 1.0f, ( float )( gameLocal.GetTime() + fas_timeOffset.GetInteger() - mVisemeStartTime ) / ( float )mBlendTime );
  148.     return( lerp );
  149. }
  150.  
  151. /*
  152. ================
  153. FAS_StartVisemeExtraction
  154.  
  155. Use a lexer to extract the phoneme data. This is a pretty slow but safe way.
  156. There shouldn't be much in the way of multiple lip syncs going on, and if there are, it should be in a non performance critical cinematic.
  157. ================
  158. */
  159. rvLipSyncData *FAS_StartVisemeExtraction( const rvDeclLipSync *ls, int time )
  160. {
  161.     rvLipSyncData    *lsd;
  162.  
  163.     lsd = new rvLipSyncData( ls, time );
  164.         
  165.     return( lsd );
  166. }
  167.  
  168. /*
  169. ================
  170. FAS_EndVisemeExtraction
  171.  
  172. Delete the workspace. FAS could use a void pointer if it wanted
  173. ================
  174. */
  175. void FAS_EndVisemeExtraction( rvLipSyncData *lsd )
  176. {
  177.     delete lsd;
  178. }
  179.  
  180. /*
  181. ================
  182. FAS_ExtractViseme
  183.  
  184. Extract the correct viseme frame from the lexer
  185. ================
  186. */
  187. void FAS_ExtractViseme( rvLipSyncData *lsd, int time )
  188. {
  189.     idToken            token;
  190.     rvViseme        *viseme;
  191.     idStr            phoneme, duration;
  192.     int                index, intensity;
  193.  
  194.     // Make sure not to return any garbage
  195.     lsd->ClearFlags();
  196.  
  197.     // Grab all the visemes, phrases and emotions until we are current
  198.     while( lsd->Ready( time ) )
  199.     {
  200.         if( !lsd->ReadToken( &token ) )
  201.         {
  202.             lsd->SetFlags( FAS_ENDED );
  203.             return;
  204.         }
  205.  
  206.         if( token == "<" )
  207.         {
  208.             // Extract phrase
  209.             if( !lsd->ReadToken( &token ) )
  210.             {
  211.                 common->Printf( "Failed to parse phrase from phoneme string\n" );
  212.                 lsd->SetFlags( FAS_ENDED );
  213.                 return;
  214.             }
  215.  
  216.             lsd->SetLastPhrase( token );
  217.             lsd->ExpectTokenString( ">" );
  218.  
  219.             lsd->SetFlags( FAS_NEW_PHRASE ); 
  220.         }
  221.         else if( token == "{" )
  222.         {
  223.             // Extract emotion
  224.             if( !lsd->ReadToken( &token ) )
  225.             {
  226.                 common->Printf( "Failed to parse emotion from phoneme string\n" );
  227.                 lsd->SetFlags( FAS_ENDED );
  228.                 return;
  229.             }
  230.  
  231.             lsd->SetEmotion( token );
  232.             lsd->ExpectTokenString( "}" );
  233.  
  234.             lsd->SetFlags( FAS_NEW_EMOTION );
  235.         }
  236.         else
  237.         {
  238.             // Extract phoneme data
  239.             index = 0;
  240.             phoneme = idStr( token[index] );
  241.             if( isupper( phoneme[0] ) )
  242.             {
  243.                 index++;
  244.                 phoneme += token[index];
  245.             }
  246.     
  247.             // Extract duration
  248.             index++;
  249.  
  250.             duration = idStr( token[index] );
  251.             index++;
  252.             if( isdigit( token[index] ) )
  253.             {
  254.                 duration += token[index];
  255.                 index++;
  256.             }
  257.  
  258.             // Extract intensity
  259.             intensity = ( token[index] - 'a' ) * 4;
  260.             intensity += fas_intensityBias.GetInteger();
  261.  
  262.             // Extract the viseme data for the selected viseme
  263.             viseme = NULL;
  264.  
  265.             if( intensity > fas_threshhold0.GetInteger() )
  266.             {
  267.                 visemeTable100->Get( phoneme, &viseme );
  268.             }
  269.             if( intensity > fas_threshhold1.GetInteger() )
  270.             {
  271.                 visemeTable66->Get( phoneme, &viseme );
  272.             }
  273.             else
  274.             {
  275.                 visemeTable33->Get( phoneme, &viseme );
  276.             }
  277.  
  278.             if( !viseme )
  279.             {
  280.                 common->Printf( "FAS: Failed to find phoneme %s intensity %d", phoneme.c_str(), intensity );
  281.                 lsd->SetFlags( FAS_ENDED );
  282.                 return;
  283.             }
  284.  
  285.             lsd->SetFrame( viseme->GetFrame() );
  286.             lsd->SetNextTokenTime( atol( duration ) * 10 );
  287.             lsd->SetBlendTime( int( viseme->GetBlendTime() * fas_blendBias.GetFloat() ) );
  288.             lsd->SetFlags( FAS_NEW_VISEME );
  289.         }
  290.     }
  291. }
  292.  
  293. /*
  294. ================
  295. FAS_Reload_f
  296. ================
  297. */
  298. void FAS_Reload_f( const idCmdArgs &args )
  299. {
  300.     // mekberg: disable non pre-cached warnings
  301.     fileSystem->SetIsFileLoadingAllowed( true );
  302.  
  303.     FAS_LoadPhonemes( phonemeFile.c_str() );
  304.  
  305.     fileSystem->SetIsFileLoadingAllowed( false );
  306. }
  307.  
  308. /*
  309. ================
  310. FAS_Init
  311. ================
  312. */
  313. bool FAS_Init( const char *visemes )
  314. {
  315.     // jnewquist: Tag scope and callees to track allocations using "new".
  316.     MEM_SCOPED_TAG( tag, MA_ANIM );
  317.  
  318.     cmdSystem->AddCommand( "reloadFAS", FAS_Reload_f, CMD_FL_SYSTEM, "reloads the viseme data" );
  319.     return( FAS_LoadPhonemes( visemes ) );
  320. }
  321.  
  322. /*
  323. ================
  324. FAS_Shutdown
  325. ================
  326. */
  327. void FAS_Shutdown( void )
  328. {
  329.     phonemeFile.Clear();
  330.     visemeTable100->Clear();
  331.     visemeTable66->Clear();
  332.     visemeTable33->Clear();
  333.  
  334.     cmdSystem->RemoveCommand( "reloadFAS" );
  335. }
  336.  
  337. /*
  338. ================
  339. idAFAttachment::EndLipSyncing
  340. ================
  341. */
  342. void idAFAttachment::EndLipSyncing( void )
  343. {
  344.     frameBlend_t frameBlend = { 0, 0, 0, 1.0f, 0.0f };
  345.     animator.SetFrame( ANIMCHANNEL_TORSO, lipSyncAnim, frameBlend );
  346.  
  347.     animator.CycleAnim( ANIMCHANNEL_HEAD, animator.GetAnim( "emotion_idle" ), gameLocal.time, 200 );
  348.     animator.CycleAnim( ANIMCHANNEL_EYELIDS, animator.GetAnim( "emotion_idle" ), gameLocal.time, 200 );
  349.  
  350.     FAS_EndVisemeExtraction( lipSyncData );
  351.     lipSyncData = NULL;
  352. }
  353.  
  354. /*
  355. ================
  356. idAFAttachment::StartLipSyncing
  357. ================
  358. */
  359. int idAFAttachment::StartLipSyncing( const char *speechDecl ) 
  360. {
  361.     int        length;
  362.  
  363.     length = 0;
  364.  
  365.     // Clean up any spurious data
  366.     EndLipSyncing();
  367.  
  368.     // Start a new lipsync if there is one
  369.     if( speechDecl[0] ) 
  370.     {
  371.         const rvDeclLipSync    *lipSync;
  372.         int                    emotion;
  373.         idStr                anim;
  374.  
  375.         lipSync = declManager->FindLipSync( speechDecl );
  376.         lipSyncData = FAS_StartVisemeExtraction( lipSync, gameLocal.GetTime() );
  377.  
  378.         // Output debug info
  379.         if( lipSync->GetDescription() && fas_debug.GetInteger() ) 
  380.         {
  381.             gameLocal.Printf( "Name: %s\n", speechDecl );
  382.             gameLocal.Printf( "Sub: %s\n", lipSync->GetDescription().c_str() );
  383.             gameLocal.Printf( "Lip: %s\n", lipSync->GetLipSyncData() );
  384.         }
  385.  
  386.         // Start the associated sound
  387.         refSound.diversity = 0.0f; 
  388.         renderEntity.referenceSoundHandle = refSound.referenceSoundHandle;
  389.         StartSoundShader( declManager->FindSound( lipSync->GetName() ), SND_CHANNEL_VOICE, refSound.parms.soundShaderFlags | SSF_IS_VO, false, &length );
  390.  
  391.         // Start the default emotion
  392.         anim = "emotion_";
  393.         anim += lipSyncData->GetEmotion();
  394.         emotion = animator.GetAnim( anim );
  395.         animator.CycleAnim( ANIMCHANNEL_HEAD, emotion, gameLocal.time, 200 );
  396.         animator.CycleAnim( ANIMCHANNEL_EYELIDS, emotion, gameLocal.time, 200 );
  397.     }
  398.  
  399.     return( length );
  400. }
  401.  
  402. /*
  403. ================
  404. idAFAttachment::HandleLipSync
  405. ================
  406. */
  407. void idAFAttachment::HandleLipSync( void )
  408. {
  409.     idStr    anim;
  410.     int        emotion;
  411.  
  412.     if( !lipSyncData )
  413.     {
  414.         return;
  415.     }
  416.  
  417.     FAS_ExtractViseme( lipSyncData, gameLocal.GetTime() + fas_timeOffset.GetInteger() );
  418.     if( lipSyncData->HasEnded() ) 
  419.     {
  420.         EndLipSyncing();
  421.         return;
  422.     }
  423.  
  424.     // If frame non zero - blend to it as a new viseme
  425.     if( lipSyncData->GetFrame() || lipSyncData->GetLastFrame() )
  426.     {
  427.         frameBlend_t    frameBlend;
  428.  
  429.         frameBlend.cycleCount = 0;
  430.         frameBlend.frame1 = idMath::ClampInt( 0, 120, lipSyncData->GetLastFrame() - 1 );
  431.         frameBlend.frame2 = idMath::ClampInt( 0, 120, lipSyncData->GetFrame() - 1 );
  432.         frameBlend.frontlerp = lipSyncData->GetFrontLerp();
  433.         frameBlend.backlerp = 1.0f - frameBlend.frontlerp;
  434.  
  435.         animator.SetFrame( ANIMCHANNEL_TORSO, lipSyncAnim, frameBlend );
  436.  
  437.         if( fas_debug.GetInteger() > 1 )
  438.         {
  439.             common->Printf( "Blending: %d (%2f) -> %d (%2f)\n", frameBlend.frame1, frameBlend.frontlerp, frameBlend.frame2, frameBlend.backlerp );
  440.         }
  441.     }
  442.  
  443.     // If an embedded emotion command, play it.
  444.     if( lipSyncData->HasNewEmotion() )
  445.     {
  446.         anim = "emotion_";
  447.         anim += lipSyncData->GetEmotion();
  448.         emotion = animator.GetAnim( anim );
  449.         animator.CycleAnim( ANIMCHANNEL_HEAD, emotion, gameLocal.time, 200 );
  450.         animator.CycleAnim( ANIMCHANNEL_EYELIDS, emotion, gameLocal.time, 200 );
  451.  
  452.         if( fas_debug.GetInteger() )
  453.         {
  454.             common->Printf( "Emotion: %s\n", lipSyncData->GetEmotion().c_str() );
  455.         }
  456.     }
  457.  
  458.     // If a new phrase, display in debug mode
  459.     if( fas_debug.GetInteger() && lipSyncData->HasNewPhrase() )
  460.     {
  461.         common->Printf( "Phrase: %s(%i)\n", lipSyncData->GetLastPhrase().c_str(), lipSyncData->GetFrame() );
  462.     }
  463. }
  464.  
  465. // end
  466.