Team Fortress 2 Source Code as on 22/4/2020
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.

275 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // A class representing session state for the SFM
  4. //
  5. //=============================================================================
  6. #include "sfmobjects/exportfacialanimation.h"
  7. #include "movieobjects/dmeclip.h"
  8. #include "movieobjects/dmeanimationset.h"
  9. #include "movieobjects/dmegamemodel.h"
  10. #include "movieobjects/dmetrackgroup.h"
  11. #include "movieobjects/dmetrack.h"
  12. #include "movieobjects/dmesound.h"
  13. #include "movieobjects/dmelog.h"
  14. #include "movieobjects/dmechannel.h"
  15. //-----------------------------------------------------------------------------
  16. // Contains export information
  17. //-----------------------------------------------------------------------------
  18. struct ExportInfo_t
  19. {
  20. CDmeFilmClip *m_pMovie;
  21. CDmeFilmClip *m_pShot;
  22. CDmeAnimationSet *m_pAnimationSet;
  23. DmeTime_t m_tExportStart;
  24. DmeTime_t m_tExportEnd;
  25. };
  26. //-----------------------------------------------------------------------------
  27. // Used to transform channel data into export time
  28. //-----------------------------------------------------------------------------
  29. static void ComputeExportChannelScaleBias( double *pScale, DmeTime_t *pBias, ExportInfo_t &info, CDmeChannel *pChannel )
  30. {
  31. DmeClipStack_t channelToGlobal;
  32. if ( pChannel->BuildClipStack( &channelToGlobal, info.m_pMovie, info.m_pShot ) )
  33. {
  34. DmeTime_t tOffset = CDmeClip::FromChildMediaTime( channelToGlobal, DMETIME_ZERO, false );
  35. DmeTime_t tScale = CDmeClip::FromChildMediaTime( channelToGlobal, DmeTime_t( 1.0f ), false );
  36. *pBias = tOffset - info.m_pShot->GetStartTime();
  37. *pScale = ( tScale - tOffset ).GetSeconds();
  38. }
  39. }
  40. static void GetExportTimeRange( DmeTime_t *pExportStart, DmeTime_t *pExportEnd, CDmeFilmClip *pShot )
  41. {
  42. *pExportStart = DMETIME_ZERO;
  43. *pExportEnd = pShot->GetDuration();
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Adds a log layer to the list of logs for export
  47. //-----------------------------------------------------------------------------
  48. static void AddLogLayerForExport( ExportInfo_t &info, CDmElement *pRoot, const char *pControlName, CDmeChannel *pChannel )
  49. {
  50. CDmeLog *pLog = pChannel->GetLog();
  51. if ( !pLog || pLog->GetNumLayers() == 0 )
  52. return;
  53. CDmrElementArray<> animations( pRoot, "animations" );
  54. DmeTime_t tBias;
  55. double flScale;
  56. ComputeExportChannelScaleBias( &flScale, &tBias, info, pChannel );
  57. // Only export the base layer
  58. CDmeLogLayer* pLogLayer = pLog->GetLayer( 0 )->Copy();
  59. pLogLayer->SetName( pControlName );
  60. pLogLayer->ScaleBiasKeyTimes( flScale, tBias );
  61. // Forcibly add keys @ the start + end time
  62. DmeTime_t tStartTime = ( info.m_tExportStart - tBias ) / flScale;
  63. DmeTime_t tEndTime = ( info.m_tExportEnd - tBias ) / flScale;
  64. pLogLayer->InsertKeyFromLayer( info.m_tExportStart, pLog->GetLayer(0), tStartTime );
  65. pLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pLog->GetLayer(0), tEndTime );
  66. pLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
  67. animations.AddToTail( pLogLayer );
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Exports animations
  71. //-----------------------------------------------------------------------------
  72. static void ExportAnimations( ExportInfo_t &info, CDmElement *pRoot )
  73. {
  74. CDmrElementArray<> animations( pRoot, "animations", true );
  75. // Build a list of all controls
  76. const CDmaElementArray< CDmElement > &controls = info.m_pAnimationSet->GetControls();
  77. int nControlCount = controls.Count();
  78. for ( int i = 0; i < nControlCount; ++i )
  79. {
  80. CDmElement *pControl = controls[i];
  81. if ( !pControl || pControl->GetValue<bool>( "transform" ) )
  82. continue;
  83. bool bIsStereo = pControl->GetValue<bool>( "combo" );
  84. if ( bIsStereo )
  85. {
  86. CDmeChannel *pValueChannel = pControl->GetValueElement<CDmeChannel>( "valuechannel" );
  87. CDmeChannel *pBalanceChannel = pControl->GetValueElement<CDmeChannel>( "balancechannel" );
  88. CDmeLog *pValueLog = pValueChannel->GetLog();
  89. CDmeLog *pBalanceLog = pBalanceChannel->GetLog();
  90. if ( pValueLog && pBalanceLog && pValueLog->GetNumLayers() != 0 && pBalanceLog->GetNumLayers() != 0 )
  91. {
  92. DmeTime_t tValueBias, tBalanceBias;
  93. double flValueScale, flBalanceScale;
  94. ComputeExportChannelScaleBias( &flValueScale, &tValueBias, info, pValueChannel );
  95. ComputeExportChannelScaleBias( &flBalanceScale, &tBalanceBias, info, pBalanceChannel );
  96. // Make copy to maintain log layer types
  97. CDmeLogLayer *pValueLogLayer = pValueLog->GetLayer( 0 )->Copy();
  98. CDmeLogLayer *pBalanceLogLayer = pBalanceLog->GetLayer( 0 )->Copy();
  99. pValueLogLayer->ScaleBiasKeyTimes( flValueScale, tValueBias );
  100. pBalanceLogLayer->ScaleBiasKeyTimes( flBalanceScale, tBalanceBias );
  101. // Forcibly insert keys @ start + end times.
  102. DmeTime_t tStartTime = ( info.m_tExportStart - tValueBias ) / flValueScale;
  103. DmeTime_t tEndTime = ( info.m_tExportEnd - tValueBias ) / flValueScale;
  104. pValueLogLayer->InsertKeyFromLayer( info.m_tExportStart, pValueLog->GetLayer(0), tStartTime );
  105. pValueLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pValueLog->GetLayer(0), tEndTime );
  106. tStartTime = ( info.m_tExportStart - tBalanceBias ) / flBalanceScale;
  107. tEndTime = ( info.m_tExportEnd - tBalanceBias ) / flBalanceScale;
  108. pBalanceLogLayer->InsertKeyFromLayer( info.m_tExportStart, pBalanceLog->GetLayer(0), tStartTime );
  109. pBalanceLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pBalanceLog->GetLayer(0), tEndTime );
  110. pValueLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
  111. pBalanceLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
  112. char pControlName[512];
  113. Q_snprintf( pControlName, sizeof(pControlName), "value_%s", pControl->GetName() );
  114. pValueLogLayer->SetName( pControlName );
  115. animations.AddToTail( pValueLogLayer );
  116. Q_snprintf( pControlName, sizeof(pControlName), "balance_%s", pControl->GetName() );
  117. pBalanceLogLayer->SetName( pControlName );
  118. animations.AddToTail( pBalanceLogLayer );
  119. }
  120. }
  121. else
  122. {
  123. CDmeChannel *pChannel = pControl->GetValueElement<CDmeChannel>( "channel" );
  124. AddLogLayerForExport( info, pRoot, pControl->GetName(), pChannel );
  125. }
  126. if ( pControl->GetValue<bool>( "multi" ) )
  127. {
  128. char pControlName[512];
  129. Q_snprintf( pControlName, sizeof(pControlName), "multi_%s", pControl->GetName() );
  130. CDmeChannel *pMultiChannel = pControl->GetValueElement<CDmeChannel>( "multilevelchannel" );
  131. AddLogLayerForExport( info, pRoot, pControlName, pMultiChannel );
  132. }
  133. }
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Helper to export sounds
  137. //-----------------------------------------------------------------------------
  138. static void ExportSounds( ExportInfo_t &info, CDmElement *pRoot, CDmeClip *pClip, DmeTime_t tOffset )
  139. {
  140. CDmrElementArray<> sounds( pRoot, "sounds", true );
  141. DmeClipStack_t soundToGlobal;
  142. int gc = pClip->GetTrackGroupCount();
  143. for ( int i = 0; i < gc; ++i )
  144. {
  145. CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
  146. DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeSoundClip, pTrackGroup, pTrack, pSoundClip )
  147. const char *pGameSoundName = pSoundClip->m_Sound->m_GameSoundName;
  148. if ( !pGameSoundName || !pGameSoundName[0] )
  149. continue;
  150. if ( pSoundClip->IsMute() )
  151. continue;
  152. if ( !pSoundClip->BuildClipStack( &soundToGlobal, info.m_pMovie, pClip ) )
  153. continue;
  154. DmeTime_t tStart = CDmeClip::FromChildMediaTime( soundToGlobal, DMETIME_ZERO, false );
  155. DmeTime_t tEnd = CDmeClip::FromChildMediaTime( soundToGlobal, pSoundClip->GetDuration(), false );
  156. tStart -= tOffset;
  157. tEnd -= tOffset;
  158. if ( tStart >= info.m_tExportEnd || tEnd <= info.m_tExportStart )
  159. continue;
  160. const char *pName = pSoundClip->GetName();
  161. CDmElement *pSoundEvent = CreateElement<CDmElement>( pName );
  162. pSoundEvent->SetValue( "start", tStart.GetTenthsOfMS() );
  163. pSoundEvent->SetValue( "end", tEnd.GetTenthsOfMS() );
  164. pSoundEvent->SetValue( "gamesound", pGameSoundName );
  165. sounds.AddToTail( pSoundEvent );
  166. DMETRACKGROUP_FOREACH_CLIP_TYPE_END()
  167. }
  168. }
  169. static void ExportSounds_R( ExportInfo_t &info, CDmElement *pRoot, CDmeClip *pClip, DmeTime_t tOffset )
  170. {
  171. ExportSounds( info, pRoot, pClip, tOffset );
  172. // Recurse
  173. DmeClipStack_t childToGlobal;
  174. int gc = pClip->GetTrackGroupCount();
  175. for ( int i = 0; i < gc; ++i )
  176. {
  177. CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
  178. DMETRACKGROUP_FOREACH_CLIP_START( pTrackGroup, pTrack, pChild )
  179. if ( !pChild->BuildClipStack( &childToGlobal, info.m_pMovie, pClip ) )
  180. continue;
  181. DmeTime_t tStart = CDmeClip::FromChildMediaTime( childToGlobal, DMETIME_ZERO, false );
  182. DmeTime_t tEnd = CDmeClip::FromChildMediaTime( childToGlobal, pChild->GetDuration(), false );
  183. tStart -= tOffset;
  184. tEnd -= tOffset;
  185. if ( tStart >= info.m_tExportEnd || tEnd <= info.m_tExportStart )
  186. continue;
  187. ExportSounds_R( info, pRoot, pChild, tOffset );
  188. DMETRACKGROUP_FOREACH_CLIP_END()
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Exports sounds, default implementation
  193. //-----------------------------------------------------------------------------
  194. static void ExportSounds( ExportInfo_t &info, CDmElement *pRoot )
  195. {
  196. DmeTime_t tOffset = info.m_pShot->GetStartTime();
  197. ExportSounds( info, pRoot, info.m_pMovie, tOffset );
  198. ExportSounds_R( info, pRoot, info.m_pShot, tOffset );
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Exports an .fac file
  202. //-----------------------------------------------------------------------------
  203. bool ExportFacialAnimation( const char *pFileName, CDmeFilmClip *pMovie, CDmeFilmClip *pShot, CDmeAnimationSet *pAnimationSet )
  204. {
  205. if ( !pMovie || !pShot || !pAnimationSet )
  206. return false;
  207. const char *pFileFormat = "facial_animation";
  208. CDmElement *pRoot = CreateElement< CDmElement >( pAnimationSet->GetName() );
  209. ExportInfo_t info;
  210. info.m_pMovie = pMovie;
  211. info.m_pShot = pShot;
  212. info.m_pAnimationSet = pAnimationSet;
  213. GetExportTimeRange( &info.m_tExportStart, &info.m_tExportEnd, pShot );
  214. CDmeGameModel *pGameModel = pAnimationSet->GetValueElement<CDmeGameModel>( "gameModel" );
  215. if ( pGameModel )
  216. {
  217. pRoot->SetValue( "gamemodel", pGameModel->GetModelName() );
  218. }
  219. ExportAnimations( info, pRoot );
  220. ExportSounds( info, pRoot );
  221. pRoot->SetFileId( DMFILEID_INVALID, TD_DEEP );
  222. const char *pEncoding = "keyvalues2_flat";
  223. bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pEncoding, pFileFormat, pRoot );
  224. DestroyElement( pRoot, TD_DEEP );
  225. return bOk;
  226. }