BVB Source Codes

CRYENGINE Show LipSync_TransitionQueue.cpp Source code

Return Download CRYENGINE: download LipSync_TransitionQueue.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. // -------------------------------------------------------------------------
  4. //  File name:   LipSync_TransitionQueue.cpp
  5. //  Version:     v1.00
  6. //  Created:     2014-08-29 by Christian Werle.
  7. //  Description: Automatic start of facial animation when a sound is being played back.
  8. // -------------------------------------------------------------------------
  9. //  History:
  10. //
  11. ////////////////////////////////////////////////////////////////////////////
  12. #include "StdAfx.h"
  13. #include "LipSync_TransitionQueue.h"
  14.  
  15. //=============================================================================
  16. //
  17. // CLipSyncProvider_TransitionQueue
  18. //
  19. //=============================================================================
  20.  
  21. static const float LIPSYNC_START_TRANSITION_TIME = 0.1f;
  22. static const float LIPSYNC_STOP_TRANSITION_TIME = 0.1f;
  23.  
  24. uint32 CLipSyncProvider_TransitionQueue::s_lastAnimationToken = 0;
  25.  
  26. static const char* GetSoundName(const AudioControlId soundId)
  27. {
  28.         CRY_ASSERT(gEnv && gEnv->pAudioSystem);
  29.  
  30.         REINST(was retrieving the filename of given soundId;
  31.                need something similar now)
  32.         //_smart_ptr<ISound> pSound = gEnv->pAudioSystem->GetSound(soundId);
  33.         //return pSound ? pSound->GetName() : NULL;
  34.         return NULL;
  35. }
  36.  
  37. static void SetAnimationTime(::CAnimation& activatedAnim, const float fSeconds)
  38. {
  39.         CRY_ASSERT(activatedAnim.IsActivated());
  40.         CRY_ASSERT(fSeconds >= 0.0f);
  41.  
  42.         const float fAnimationDuration = activatedAnim.GetCurrentSegmentExpectedDurationSeconds();
  43.         CRY_ASSERT(fAnimationDuration >= 0.0f);
  44.  
  45.         const bool isLooping = activatedAnim.HasStaticFlag(CA_LOOP_ANIMATION);
  46.  
  47.         float fNormalizedTime = 0.0f;
  48.         if (fAnimationDuration > FLT_EPSILON)
  49.         {
  50.                 const float fAnimTimeSeconds =
  51.                   isLooping
  52.                   ? fmodf(fSeconds, fAnimationDuration)
  53.                   : std::min<float>(fSeconds, fAnimationDuration);
  54.  
  55.                 fNormalizedTime = fAnimTimeSeconds / fAnimationDuration;
  56.         }
  57.         activatedAnim.SetCurrentSegmentNormalizedTime(fNormalizedTime);
  58. }
  59.  
  60. CLipSyncProvider_TransitionQueue::CLipSyncProvider_TransitionQueue(EntityId entityId)
  61.         : m_entityId(entityId)
  62.         , m_nCharacterSlot(-1)
  63.         , m_nAnimLayer(-1)
  64.         , m_state(eS_Init)
  65.         , m_isSynchronized(false)
  66.         , m_requestedAnimId(-1)
  67.         , m_nCurrentAnimationToken(0)
  68.         , m_soundId(INVALID_AUDIO_CONTROL_ID)
  69. {
  70.         // read settings from script
  71.         if (IEntity* pEntity = GetEntity())
  72.         {
  73.                 if (SmartScriptTable pScriptTable = pEntity->GetScriptTable())
  74.                 {
  75.                         SmartScriptTable pPropertiesTable;
  76.                         if (pScriptTable->GetValue("Properties", pPropertiesTable))
  77.                         {
  78.                                 SmartScriptTable pLipSyncTable;
  79.                                 if (pPropertiesTable->GetValue("LipSync", pLipSyncTable))
  80.                                 {
  81.                                         SmartScriptTable pSettingsTable;
  82.                                         if (pLipSyncTable->GetValue("TransitionQueueSettings", pSettingsTable))
  83.                                         {
  84.                                                 pSettingsTable->GetValue("nCharacterSlot", m_nCharacterSlot);
  85.                                                 pSettingsTable->GetValue("nAnimLayer", m_nAnimLayer);
  86.                                                 pSettingsTable->GetValue("sDefaultAnimName", m_sDefaultAnimName);
  87.                                         }
  88.                                 }
  89.                         }
  90.                 }
  91.         }
  92. }
  93.  
  94. IEntity* CLipSyncProvider_TransitionQueue::GetEntity()
  95. {
  96.         return gEnv->pEntitySystem->GetEntity(m_entityId);
  97. }
  98.  
  99. ICharacterInstance* CLipSyncProvider_TransitionQueue::GetCharacterInstance()
  100. {
  101.         if (IEntity* pEnt = GetEntity())
  102.         {
  103.                 return pEnt->GetCharacter(m_nCharacterSlot);
  104.         }
  105.         else
  106.         {
  107.                 return NULL;
  108.         }
  109. }
  110.  
  111. void CLipSyncProvider_TransitionQueue::FullSerialize(TSerialize ser)
  112. {
  113.         ser.BeginGroup("LipSyncProvider_TransitionQueue");
  114.  
  115.         ser.Value("m_entityId", m_entityId);
  116.         ser.Value("m_nCharacterSlot", m_nCharacterSlot);
  117.         ser.Value("m_nAnimLayer", m_nAnimLayer);
  118.         ser.Value("m_sDefaultAnimName", m_sDefaultAnimName);
  119.         ser.EnumValue("m_state", m_state, eS_Init, eS_Stopped);
  120.         ser.Value("m_isSynchronized", m_isSynchronized);
  121.         ser.Value("m_requestedAnimId", m_requestedAnimId);
  122.         m_requestedAnimParams.Serialize(ser);
  123.         m_cachedAnim.Serialize(ser);
  124.         ser.Value("m_nCurrentAnimationToken", m_nCurrentAnimationToken);
  125.         ser.Value("m_soundId", m_soundId);
  126.  
  127.         ser.EndGroup();
  128.  
  129.         if (ser.IsReading())
  130.         {
  131.                 m_isSynchronized = false;
  132.         }
  133. }
  134.  
  135. void CLipSyncProvider_TransitionQueue::RequestLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  136. {
  137.         CRY_ASSERT(pProxy);
  138.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  139.  
  140.         if (lipSyncMethod != eLSM_None)
  141.         {
  142.                 if (ICharacterInstance* pChar = GetCharacterInstance())
  143.                 {
  144.                         FindMatchingAnim(audioTriggerId, lipSyncMethod, *pChar, &m_requestedAnimId, &m_requestedAnimParams);
  145.  
  146.                         if (m_requestedAnimId >= 0)
  147.                         {
  148.                                 const uint32 filePathCRC = pChar->GetIAnimationSet()->GetFilePathCRCByAnimID(m_requestedAnimId);
  149.                                 m_cachedAnim = CAutoResourceCache_CAF(filePathCRC);
  150.                         }
  151.                         else
  152.                         {
  153.                                 m_cachedAnim = CAutoResourceCache_CAF();
  154.                         }
  155.                 }
  156.                 else
  157.                 {
  158.                         m_cachedAnim = CAutoResourceCache_CAF();
  159.                 }
  160.         }
  161.  
  162.         m_state = eS_Requested;
  163. }
  164.  
  165. void CLipSyncProvider_TransitionQueue::StartLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  166. {
  167.         CRY_ASSERT(pProxy);
  168.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  169.         CRY_ASSERT((m_state == eS_Requested) || (m_state == eS_Unpaused));
  170.  
  171.         if (lipSyncMethod != eLSM_None)
  172.         {
  173.                 m_soundId = audioTriggerId;
  174.                 m_isSynchronized = false;
  175.  
  176.                 if (ICharacterInstance* pChar = GetCharacterInstance())
  177.                 {
  178.                         if (m_requestedAnimId >= 0)
  179.                         {
  180.                                 ISkeletonAnim* skeletonAnimation = pChar->GetISkeletonAnim();
  181.                                 const bool success = skeletonAnimation->StartAnimationById(m_requestedAnimId, m_requestedAnimParams);
  182.                                 if (success)
  183.                                 {
  184.                                         m_nCurrentAnimationToken = m_requestedAnimParams.m_nUserToken;
  185.                                         SynchronizeAnimationToSound(audioTriggerId);
  186.                                 }
  187.                                 else
  188.                                 {
  189.                                         m_nCurrentAnimationToken = -1;
  190.                                 }
  191.                         }
  192.                 }
  193.         }
  194.         m_state = eS_Started;
  195. }
  196.  
  197. void CLipSyncProvider_TransitionQueue::PauseLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  198. {
  199.         CRY_ASSERT(pProxy);
  200.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  201.         CRY_ASSERT(audioTriggerId == m_soundId);
  202.         CRY_ASSERT((m_state == eS_Started) || (m_state == eS_Unpaused));
  203.  
  204.         m_state = eS_Paused;
  205. }
  206.  
  207. void CLipSyncProvider_TransitionQueue::UnpauseLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  208. {
  209.         CRY_ASSERT(pProxy);
  210.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  211.         CRY_ASSERT(audioTriggerId == m_soundId);
  212.         CRY_ASSERT((m_state == eS_Started) || (m_state == eS_Paused));
  213.  
  214.         if (lipSyncMethod != eLSM_None)
  215.         {
  216.                 m_isSynchronized = false;
  217.                 SynchronizeAnimationToSound(audioTriggerId);
  218.         }
  219.  
  220.         m_state = eS_Unpaused;
  221. }
  222.  
  223. void CLipSyncProvider_TransitionQueue::StopLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  224. {
  225.         CRY_ASSERT(pProxy);
  226.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  227.         CRY_ASSERT((m_state == eS_Started) || (m_state == eS_Requested) || (m_state == eS_Unpaused) || (m_state == eS_Paused));
  228.  
  229.         if (lipSyncMethod != eLSM_None)
  230.         {
  231.                 if (m_state == eS_Requested)
  232.                 {
  233.                         CRY_ASSERT(m_soundId == INVALID_AUDIO_CONTROL_ID);
  234.                 }
  235.                 else
  236.                 {
  237.                         CRY_ASSERT(audioTriggerId == m_soundId);
  238.  
  239.                         if (ICharacterInstance* pChar = GetCharacterInstance())
  240.                         {
  241.                                 if (m_requestedAnimId >= 0)
  242.                                 {
  243.                                         ISkeletonAnim* skeletonAnimation = pChar->GetISkeletonAnim();
  244.  
  245.                                         // NOTE: there is no simple way to just stop the exact animation we started, but this should do too:
  246.                                         bool success = skeletonAnimation->StopAnimationInLayer(m_nAnimLayer, LIPSYNC_STOP_TRANSITION_TIME);
  247.                                         CRY_ASSERT(success);
  248.                                 }
  249.                         }
  250.  
  251.                         m_soundId = INVALID_AUDIO_CONTROL_ID;
  252.                         m_isSynchronized = false;
  253.                 }
  254.  
  255.                 m_cachedAnim = CAutoResourceCache_CAF();
  256.         }
  257.         m_state = eS_Stopped;
  258. }
  259.  
  260. void CLipSyncProvider_TransitionQueue::UpdateLipSync(IEntityAudioComponent* pProxy, const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod)
  261. {
  262.         CRY_ASSERT(pProxy);
  263.  
  264.         if (lipSyncMethod != eLSM_None)
  265.         {
  266.                 if ((m_state == eS_Started) || (m_state == eS_Unpaused))
  267.                 {
  268.                         CRY_ASSERT(audioTriggerId == m_soundId);
  269.  
  270.                         SynchronizeAnimationToSound(m_soundId);
  271.                 }
  272.         }
  273. }
  274.  
  275. void CLipSyncProvider_TransitionQueue::FillCharAnimationParams(const bool isDefaultAnim, CryCharAnimationParams* pParamsOut) const
  276. {
  277.         *pParamsOut = CryCharAnimationParams();
  278.  
  279.         pParamsOut->m_fTransTime = LIPSYNC_START_TRANSITION_TIME;
  280.         pParamsOut->m_nLayerID = m_nAnimLayer;
  281.         pParamsOut->m_nUserToken = ++s_lastAnimationToken;
  282.         pParamsOut->m_nFlags = CA_ALLOW_ANIM_RESTART;
  283.  
  284.         if (isDefaultAnim)
  285.         {
  286.                 pParamsOut->m_nFlags |= CA_LOOP_ANIMATION;
  287.                 pParamsOut->m_fPlaybackSpeed = 1.25f;
  288.         }
  289. }
  290.  
  291. void CLipSyncProvider_TransitionQueue::FindMatchingAnim(const AudioControlId audioTriggerId, const ELipSyncMethod lipSyncMethod, ICharacterInstance& character, int* pAnimIdOut, CryCharAnimationParams* pAnimParamsOut) const
  292. {
  293.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  294.         CRY_ASSERT(pAnimIdOut != NULL);
  295.         CRY_ASSERT(pAnimParamsOut != NULL);
  296.  
  297.         const char* szSoundName = ::GetSoundName(audioTriggerId);
  298.         CRY_ASSERT(szSoundName);
  299.  
  300.         // Look for an animation matching the sound name exactly
  301.  
  302.         string matchingAnimationName = PathUtil::GetFileName(szSoundName);
  303.         const IAnimationSet* pAnimSet = character.GetIAnimationSet();
  304.         int nAnimId = pAnimSet->GetAnimIDByName(matchingAnimationName.c_str());
  305.  
  306.         if (nAnimId < 0)
  307.         {
  308.                 // First fallback: look for an animation matching the sound name without the index at the end
  309.  
  310.                 int index = static_cast<int>(matchingAnimationName.length()) - 1;
  311.                 while ((index >= 0) && isdigit((unsigned char)matchingAnimationName[index]))
  312.                 {
  313.                         --index;
  314.                 }
  315.  
  316.                 if ((index > 0) && (matchingAnimationName[index] == '_'))
  317.                 {
  318.                         matchingAnimationName = matchingAnimationName.Left(index);
  319.  
  320.                         nAnimId = pAnimSet->GetAnimIDByName(matchingAnimationName.c_str());
  321.                 }
  322.         }
  323.  
  324.         bool isDefaultAnim = false;
  325.  
  326.         if (nAnimId < 0)
  327.         {
  328.                 // Second fallback: when requested use a default lip movement animation
  329.  
  330.                 if (lipSyncMethod == eLSM_MatchAnimationToSoundName)
  331.                 {
  332.                         nAnimId = pAnimSet->GetAnimIDByName(m_sDefaultAnimName.c_str());
  333.                         if (nAnimId < 0)
  334.                         {
  335.                                 CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "No '%s' default animation found for face '%s'. Automatic lip movement will not work.", m_sDefaultAnimName.c_str(), character.GetFilePath());
  336.                         }
  337.                         isDefaultAnim = true;
  338.                 }
  339.         }
  340.  
  341.         *pAnimIdOut = nAnimId;
  342.         FillCharAnimationParams(isDefaultAnim, pAnimParamsOut);
  343. }
  344.  
  345. void CLipSyncProvider_TransitionQueue::SynchronizeAnimationToSound(const AudioControlId audioTriggerId)
  346. {
  347.         CRY_ASSERT(audioTriggerId != INVALID_AUDIO_CONTROL_ID);
  348.         CRY_ASSERT(gEnv->pAudioSystem);
  349.  
  350.         if (m_isSynchronized)
  351.                 return;
  352.  
  353.         REINST(was retrieving the current playback position in milliseconds of given audioTriggerId)
  354.         //_smart_ptr<ISound> pSound = gEnv->pSoundSystem ? gEnv->pSoundSystem->GetSound(nAudioTriggerId) : NULL;
  355.  
  356.         //// Workaround for crash TFS-301214.
  357.         //// The crash happens because the sound already stopped but we didn't get the event yet.
  358.         //// The early out here assumes we will still get that event later on.
  359.         //if (!pSound)
  360.         //      return;
  361.  
  362.         //ICharacterInstance* pChar = GetCharacterInstance();
  363.         //if (!pChar)
  364.         //      return;
  365.  
  366.         //const unsigned int nSoundMillis = pSound->GetInterfaceExtended()->GetCurrentSamplePos(true);
  367.         //const float fSeconds = static_cast<float>(nSoundMillis)/1000.0f;
  368.  
  369.         //ISkeletonAnim* skeletonAnimation = pChar->GetISkeletonAnim();
  370.         //::CAnimation* pAnim = skeletonAnimation->FindAnimInFIFO(m_nCurrentAnimationToken, m_nAnimLayer);
  371.         //if (pAnim && pAnim->IsActivated())
  372.         //{
  373.         //      ::SetAnimationTime(*pAnim, fSeconds);
  374.         //      m_isSynchronized = true;
  375.         //}
  376. }
  377.  
  378. //=============================================================================
  379. //
  380. // CLipSync_TransitionQueue
  381. //
  382. //=============================================================================
  383.  
  384. void CLipSync_TransitionQueue::InjectLipSyncProvider()
  385. {
  386.         IEntity* pEntity = GetEntity();
  387.         IEntityAudioComponent* pSoundProxy = pEntity->GetOrCreateComponent<IEntityAudioComponent>();
  388.         CRY_ASSERT(pSoundProxy);
  389.         m_pLipSyncProvider.reset(new CLipSyncProvider_TransitionQueue(pEntity->GetId()));
  390.         REINST(add SetLipSyncProvider to interface)
  391.         //pSoundProxy->SetLipSyncProvider(m_pLipSyncProvider);
  392. }
  393.  
  394. void CLipSync_TransitionQueue::GetMemoryUsage(ICrySizer* pSizer) const
  395. {
  396.         pSizer->Add(*this);
  397.         if (m_pLipSyncProvider)
  398.         {
  399.                 pSizer->Add(*m_pLipSyncProvider);
  400.         }
  401. }
  402.  
  403. bool CLipSync_TransitionQueue::Init(IGameObject* pGameObject)
  404. {
  405.         SetGameObject(pGameObject);
  406.         return true;
  407. }
  408.  
  409. void CLipSync_TransitionQueue::PostInit(IGameObject* pGameObject)
  410. {
  411.         InjectLipSyncProvider();
  412. }
  413.  
  414. void CLipSync_TransitionQueue::InitClient(int channelId)
  415. {
  416. }
  417.  
  418. void CLipSync_TransitionQueue::PostInitClient(int channelId)
  419. {
  420. }
  421.  
  422. bool CLipSync_TransitionQueue::ReloadExtension(IGameObject* pGameObject, const SEntitySpawnParams& params)
  423. {
  424.         ResetGameObject();
  425.         return true;
  426. }
  427.  
  428. void CLipSync_TransitionQueue::PostReloadExtension(IGameObject* pGameObject, const SEntitySpawnParams& params)
  429. {
  430.         InjectLipSyncProvider();
  431. }
  432.  
  433. void CLipSync_TransitionQueue::FullSerialize(TSerialize ser)
  434. {
  435.         ser.BeginGroup("LipSync_TransitionQueue");
  436.  
  437.         bool bLipSyncProviderIsInjected = (m_pLipSyncProvider != NULL);
  438.         ser.Value("bLipSyncProviderIsInjected", bLipSyncProviderIsInjected);
  439.         if (bLipSyncProviderIsInjected && !m_pLipSyncProvider)
  440.         {
  441.                 CRY_ASSERT(ser.IsReading());
  442.                 InjectLipSyncProvider();
  443.         }
  444.         if (m_pLipSyncProvider)
  445.         {
  446.                 m_pLipSyncProvider->FullSerialize(ser);
  447.         }
  448.  
  449.         ser.EndGroup();
  450. }
  451.  
  452. bool CLipSync_TransitionQueue::NetSerialize(TSerialize ser, EEntityAspects aspect, uint8 profile, int pflags)
  453. {
  454.         return true;
  455. }
  456.  
  457. void CLipSync_TransitionQueue::PostSerialize()
  458. {
  459. }
  460.  
  461. void CLipSync_TransitionQueue::SerializeSpawnInfo(TSerialize ser)
  462. {
  463. }
  464.  
  465. ISerializableInfoPtr CLipSync_TransitionQueue::GetSpawnInfo()
  466. {
  467.         return NULL;
  468. }
  469.  
  470. void CLipSync_TransitionQueue::Update(SEntityUpdateContext& ctx, int updateSlot)
  471. {
  472. }
  473.  
  474. void CLipSync_TransitionQueue::HandleEvent(const SGameObjectEvent& event)
  475. {
  476. }
  477.  
  478. void CLipSync_TransitionQueue::ProcessEvent(SEntityEvent& event)
  479. {
  480. }
  481.  
  482. void CLipSync_TransitionQueue::SetChannelId(uint16 id)
  483. {
  484. }
  485.  
  486. void CLipSync_TransitionQueue::SetAuthority(bool auth)
  487. {
  488. }
  489.  
  490. void CLipSync_TransitionQueue::PostUpdate(float frameTime)
  491. {
  492. }
  493.  
  494. void CLipSync_TransitionQueue::PostRemoteSpawn()
  495. {
  496. }
  497.  
  498. void CLipSync_TransitionQueue::OnShutDown()
  499. {
  500.         IEntity* pEntity = GetEntity();
  501.         if (IEntityAudioComponent* pSoundProxy = pEntity->GetComponent<IEntityAudioComponent>())
  502.         {
  503.                 REINST(add SetLipSyncProvider to interface)
  504.                         //pSoundProxy->SetLipSyncProvider(ILipSyncProviderPtr());
  505.         }
  506. }
downloadLipSync_TransitionQueue.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top