/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "CryLegacy_precompiled.h" #include "FaceAnimSequence.h" #include "FaceAnimation.h" #include "FacialInstance.h" #include "../CharacterInstance.h" #include "VectorMap.h" #include "FaceJoystick.h" #include "FaceChannelKeyCleanup.h" #include "FaceChannelSmoothing.h" #include "CharacterManager.h" #define INVALID_CHANNEL_ID (~0) #define MIN_CHANNEL_WEIGHT (0.01f) #define PHONEME_FADE_TIME (0.1f) ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannelInterpolator::SerializeSpline(XmlNodeRef& node, bool bLoading) { if (bLoading) { string keystr = node->getAttr("Keys"); resize(0); int curPos = 0; string key = keystr.Tokenize(",", curPos); while (!key.empty()) { float time, v; int flags = 0; PREFAST_SUPPRESS_WARNING(6031) azsscanf(key, "%g:%g:%d", &time, &v, &flags); ValueType val; val[0] = v; int nKey = InsertKey(time, val); if (nKey >= 0) { SetKeyFlags(nKey, flags); } key = keystr.Tokenize(",", curPos); } ; } else { string keystr; string skey; for (int i = 0; i < num_keys(); i++) { skey.Format("%g:%g:%d,", key(i).time, key(i).value, key(i).flags); keystr += skey; } node->setAttr("Keys", keystr); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannelInterpolator::CleanupKeys(float errorMax) { FaceChannel::CleanupKeys(this, errorMax); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannelInterpolator::SmoothKeys(float sigma) { FaceChannel::GaussianBlurKeys(this, sigma); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannelInterpolator::RemoveNoise(float sigma, float threshold) { FaceChannel::RemoveNoise(this, sigma, threshold); } ////////////////////////////////////////////////////////////////////////// CFacialAnimChannel::CFacialAnimChannel(int index) { m_nFlags = 0; m_nInstanceChannelId = index; } ////////////////////////////////////////////////////////////////////////// CFacialAnimChannel::~CFacialAnimChannel() { for (size_t i = 0, count = m_splines.size(); i < count; ++i) { delete m_splines[i]; } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::SetIdentifier(CFaceIdentifierHandle ident) { m_name = ident; if (!m_effectorName.GetCRC32()) { m_effectorName = ident; } } ////////////////////////////////////////////////////////////////////////// const CFaceIdentifierHandle CFacialAnimChannel::GetIdentifier() { if (!m_name.GetCRC32()) { if (m_pEffector) { m_name = m_pEffector->GetIdentifier(); } } return m_name.GetHandle(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::SetEffector(IFacialEffector* pEffector) { m_pEffector = (CFacialEffector*)pEffector; if (m_pEffector) { m_name = m_pEffector->GetIdentifier(); m_effectorName = m_name.GetHandle(); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::CreateInterpolator() { m_splines.clear(); m_splines.push_back(new CFacialAnimChannelInterpolator); m_splines.back()->InsertKeyFloat(FacialEditorSnapTimeToFrame(0), 0); m_splines.back()->InsertKeyFloat(FacialEditorSnapTimeToFrame(0.5f), 0); m_splines.back()->InsertKeyFloat(FacialEditorSnapTimeToFrame(1), 0); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::AddInterpolator() { m_splines.push_back(new CFacialAnimChannelInterpolator); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::DeleteInterpolator(int i) { if (i >= 0 && i < int(m_splines.size())) { m_splines.erase(m_splines.begin() + i); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::CleanupKeys(float fErrorMax) { if (!m_splines.empty()) { m_splines.back()->CleanupKeys(fErrorMax); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::SmoothKeys(float sigma) { if (!m_splines.empty()) { m_splines.back()->SmoothKeys(sigma); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimChannel::RemoveNoise(float sigma, float threshold) { if (!m_splines.empty()) { m_splines.back()->RemoveNoise(sigma, threshold); } } ////////////////////////////////////////////////////////////////////////// float CFacialAnimChannel::Evaluate(float t) { float total = 0; for (size_t i = 0, count = m_splines.size(); i < count; ++i) { float v = 0; m_splines[i]->interpolate(t, v); total += v; } return total; } #ifdef FACE_STORE_ASSET_VALUES void CFacialAnimChannel::SetName(const char* name) { m_name = g_pCharacterManager->GetFacialAnimation()->CreateIdentifierHandle(name); } #endif ////////////////////////////////////////////////////////////////////////// // CFacialAnimSequence ////////////////////////////////////////////////////////////////////////// CFacialAnimSequence::CFacialAnimSequence(CFacialAnimation* pFaceAnim) { m_nRefCount = 0; m_name = "Default Sequence"; m_pFaceAnim = pFaceAnim; m_bInMemory = false; m_pStreamingData = NULL; } ////////////////////////////////////////////////////////////////////////// CFacialAnimSequence::~CFacialAnimSequence() { if (m_pStream) { m_pStream->Abort(); } CRY_ASSERT(!m_pStreamingData); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::Release() { if (--m_nRefCount <= 0) { delete this; } } ////////////////////////////////////////////////////////////////////////// bool CFacialAnimSequence::StartStreaming(const char* sFilename) { if (m_pStream) { m_pStream->Abort(); m_pStream = nullptr; } CRY_ASSERT(!m_pStreamingData); m_pStreamingData = new Data; m_pStreamingData->m_nValidateID = m_data.m_nValidateID; m_pStreamingData->m_nProceduralChannelsValidateID = m_data.m_nProceduralChannelsValidateID; m_pStreamingData->m_nSoundEntriesValidateID = m_data.m_nSoundEntriesValidateID; StreamReadParams params; params.dwUserData = 0; params.nSize = 0; params.pBuffer = NULL; params.nLoadTime = 10000; params.nMaxLoadTime = 1000; m_pStream = g_pISystem->GetStreamEngine()->StartRead(eStreamTaskTypeAnimation, sFilename, this, ¶ms); return m_pStream != NULL; } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::SetName(const char* sNewName) { m_pFaceAnim->RenameAnimSequence(this, sNewName); }; ////////////////////////////////////////////////////////////////////////// IFacialAnimChannel* CFacialAnimSequence::GetChannel(int nIndex) { CRY_ASSERT(nIndex >= 0 && nIndex < (int)m_data.m_channels.size()); return m_data.m_channels[nIndex]; } ////////////////////////////////////////////////////////////////////////// IFacialAnimChannel* CFacialAnimSequence::CreateChannel() { return m_data.CreateChannel(); } ////////////////////////////////////////////////////////////////////////// IFacialAnimChannel* CFacialAnimSequence::CreateChannelGroup() { return m_data.CreateChannelGroup(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::RemoveChannel(IFacialAnimChannel* pChannelToRemove) { if (pChannelToRemove) { m_data.m_nValidateID++; std::vector<bool> indicesToDelete; indicesToDelete.resize(m_data.m_channels.size()); std::fill(indicesToDelete.begin(), indicesToDelete.end(), false); for (int i = 0, end = (int)m_data.m_channels.size(); i < end; ++i) { for (IFacialAnimChannel* pChannel = m_data.m_channels[i]; pChannel; pChannel = pChannel->GetParent()) { if (pChannel == pChannelToRemove) { indicesToDelete[i] = true; } } } std::vector<_smart_ptr<CFacialAnimChannel> >::iterator it = m_data.m_channels.begin(); for (std::vector<bool>::iterator shouldDel = indicesToDelete.begin(), end = indicesToDelete.end(); shouldDel != end; ++shouldDel) { if (*shouldDel) { it = m_data.m_channels.erase(it); } else { ++it; } } for (int i = 0, end = (int)m_data.m_channels.size(); i < end; i++) { m_data.m_channels[i]->SetInstanceChannelId(i); } } } ////////////////////////////////////////////////////////////////////////// int CFacialAnimSequence::GetSoundEntryCount() { return m_data.m_soundEntries.size(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::InsertSoundEntry(int index) { if (index < 0 || index > int(m_data.m_soundEntries.size())) { g_pILog->LogError("CFacialAnimSequence: Inserting sound entry at invalid location (Code bug)."); } else { m_data.m_soundEntries.insert(m_data.m_soundEntries.begin() + index, CFacialAnimSoundEntry()); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::DeleteSoundEntry(int index) { if (index < 0 || index > int(m_data.m_soundEntries.size())) { g_pILog->LogError("CFacialAnimSequence: Deleting non-existent sound entry (Code bug)."); } else { m_data.m_soundEntries.erase(m_data.m_soundEntries.begin() + index); } } ////////////////////////////////////////////////////////////////////////// IFacialAnimSoundEntry* CFacialAnimSequence::GetSoundEntry(int index) { return ((index >= 0 && index < int(m_data.m_soundEntries.size())) ? &m_data.m_soundEntries[index] : 0); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::Animate(const QuatTS& rAnimLocationNext, CFacialAnimSequenceInstance* pInstance, float fTime) { if (IsInMemory()) { if (!pInstance) { return; } UpdateProceduralChannels(); if (pInstance->m_channels.size() != m_data.m_channels.size() || pInstance->m_nValidateID != m_data.m_nValidateID) { // Sequence was changed, must rebind all channels. pInstance->BindChannels(pInstance->m_pAnimContext, this); } // MichaelS - Loop through balance channels and evaluate them first. { float* balances = &pInstance->m_pAnimContext->GetInstance()->GetState()->m_balance[0]; int* stateIndices = 0; uint32 num = pInstance->m_balanceChannelStateIndices.size(); if (num) { stateIndices = &pInstance->m_balanceChannelStateIndices[0]; } uint32 end = pInstance->m_balanceChannelEntries.size(); for (uint32 i = 0; i < end; ++i) { CFacialAnimSequenceInstance::BalanceChannelEntry& entry = pInstance->m_balanceChannelEntries[i]; CRY_ASSERT(entry.nChannelIndex >= 0); CFacialAnimChannel* pChannel = m_data.m_channels[entry.nChannelIndex]; entry.fEvaluatedBalance = pChannel->Evaluate(fTime); // Loop through all the morphs in the state array that refer to this balance. for (int stateIndexIndex = 0; stateIndexIndex < entry.nMorphIndexCount; ++stateIndexIndex) { int stateIndex = stateIndices[entry.nMorphIndexStartIndex + stateIndexIndex]; balances[stateIndex] = entry.fEvaluatedBalance * entry.fEvaluatedBalance * entry.fEvaluatedBalance; } } } float fPhonemeStrength = 1.0f; float fMorphTargetVertexDrag = 1.0f; float fProceduralStrength = 0.0f; bool bHasBakedLipsynch = false; CCharInstance* pCharacter = pInstance->m_pAnimContext->GetInstance()->GetMasterCharacter(); //pCharacter->m_Morphing.m_fMorphVertexDrag = 1.0f; int numChannels = (int)m_data.m_channels.size(); for (int i = 0; i < numChannels; i++) { CFacialAnimSequenceInstance::ChannelInfo& chinfo = pInstance->m_channels[i]; CFacialAnimChannel* pChannel = m_data.m_channels[i]; int flags = pChannel->GetFlags(); if (flags & IFacialAnimChannel::FLAG_BAKED_LIPSYNC_GROUP) { bHasBakedLipsynch = true; continue; } if (flags & IFacialAnimChannel::FLAG_PHONEME_STRENGTH) { fPhonemeStrength = pChannel->Evaluate(fTime); continue; } if (flags & IFacialAnimChannel::FLAG_PROCEDURAL_STRENGTH) { fProceduralStrength = pChannel->Evaluate(fTime); continue; } if (flags & IFacialAnimChannel::FLAG_VERTEX_DRAG) { fMorphTargetVertexDrag = 0.1f + pChannel->Evaluate(fTime) * 2.0f; //pCharacter->m_Morphing.m_fMorphVertexDrag = fMorphTargetVertexDrag; continue; } if (flags & IFacialAnimChannel::FLAG_LIPSYNC_CATEGORY_STRENGTH) { IFacialEffector* pEffector = pChannel->GetEffector(); float fStrength = pChannel->Evaluate(fTime); if (pEffector) { for (size_t subEffectorIndex = 0, subEffectorCount = pEffector->GetSubEffectorCount(); subEffectorIndex < subEffectorCount; ++subEffectorIndex) { IFacialEffector* pSubEffector = pEffector->GetSubEffector(subEffectorIndex); int index = pSubEffector->GetIndexInState(); if (index >= 0) { pInstance->m_pAnimContext->SetLipsyncStrength(index, fStrength); } } } continue; } if (!chinfo.bUse || chinfo.pEffector == NULL) { continue; } float w = pChannel->Evaluate(fTime); //if (fabs(w) > MIN_CHANNEL_WEIGHT) { SFacialEffectorChannel* pEffectorAnimChannel = pInstance->m_pAnimContext->GetChannel(chinfo.nChannelId); if (pEffectorAnimChannel) { pEffectorAnimChannel->fCurrWeight = w; pEffectorAnimChannel->fWeight = w; if (chinfo.nBalanceChannelListIndex >= 0) { pEffectorAnimChannel->fBalance = pInstance->m_balanceChannelEntries[chinfo.nBalanceChannelListIndex].fEvaluatedBalance; } } else { CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_ERROR, "CFacialAnimSequence::Animate: effector animation channel doesn't exist"); return; } } } for (int i = 0; i < CProceduralChannelSet::ChannelType_count; i++) { CFacialAnimSequenceInstance::ChannelInfo& chinfo = pInstance->m_proceduralChannels[i]; CProceduralChannel* pChannel = m_data.m_proceduralChannels.GetChannel(static_cast<CProceduralChannelSet::ChannelType>(i)); if (!chinfo.bUse || chinfo.pEffector == NULL) { continue; } float fWeight; pChannel->GetInterpolator()->interpolate(fTime, fWeight); fWeight *= fProceduralStrength; if (fabs(fWeight) > MIN_CHANNEL_WEIGHT) { SFacialEffectorChannel* pEffectorAnimChannel = pInstance->m_pAnimContext->GetChannel(chinfo.nChannelId); CRY_ASSERT(pEffectorAnimChannel); if (pEffectorAnimChannel) { pEffectorAnimChannel->fCurrWeight = fWeight; pEffectorAnimChannel->fWeight = fWeight; if (chinfo.nBalanceChannelListIndex >= 0) { pEffectorAnimChannel->fBalance = pInstance->m_balanceChannelEntries[chinfo.nBalanceChannelListIndex].fEvaluatedBalance; } } } } // It is possible for the animator to override the lipsync with a manual channel. These channels are stored in the // 'BakedLipSync' folder. We need to find all such channels and pass it to the lip synching code. CFacialSentence::OverridesMap overriddenPhonemes; if (bHasBakedLipsynch) { AZStd::vector<CFaceIdentifierHandle, AZ::AZStdAlloc<AZ::LegacyAllocator>> foundChannels; foundChannels.reserve(200); CFaceIdentifierHandle bakedLipSyncHandle = m_pFaceAnim->CreateIdentifierHandle("BakedLipSync"); for (int channelIndex = 0, channelCount = (int)m_data.m_channels.size(); channelIndex < channelCount; ++channelIndex) { CFacialAnimChannel* pChannel = m_data.m_channels[channelIndex]; IFacialAnimChannel* pChannelParent = (pChannel ? pChannel->GetParent() : 0); CFaceIdentifierHandle parentName = (pChannelParent ? pChannelParent->GetIdentifier() : CFaceIdentifierHandle()); if (m_pFaceAnim) { bool inFolder = bakedLipSyncHandle.GetCRC32() == parentName.GetCRC32(); if (inFolder) { CFaceIdentifierHandle name = (pChannel ? pChannel->GetIdentifier() : CFaceIdentifierHandle()); foundChannels.push_back(name); } } } overriddenPhonemes.SwapElementsWithVector(foundChannels); } for (int i = 0, count = m_data.m_soundEntries.size(); i < count; ++i) { if (m_data.m_soundEntries[i].m_pSentence) { m_data.m_soundEntries[i].m_pSentence->Animate(rAnimLocationNext, pInstance->m_pAnimContext, fTime - m_data.m_soundEntries[i].m_startTime, fPhonemeStrength * Console::GetInst().ca_lipsync_phoneme_strength, overriddenPhonemes); } } } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::SerializeLoad(Data& data, XmlNodeRef& xmlNode, ESerializationFlags flags) { ++data.m_nValidateID; if (flags & SFLAG_ANIMATION) { data.m_channels.clear(); } if (flags & SFLAG_SOUND_ENTRIES) { data.m_soundEntries.clear(); } if (flags & SFLAG_CAMERA_PATH) { data.m_cameraPathPosition.clear(); data.m_cameraPathOrientation.clear(); data.m_cameraPathFOV.clear(); } if (flags & SFLAG_ANIMATION) { xmlNode->getAttr("Flags", data.m_nFlags); xmlNode->getAttr("StartTime", data.m_timeRange.start); xmlNode->getAttr("EndTime", data.m_timeRange.end); data.m_joystickFile = xmlNode->getAttr("Joysticks"); } // Load all sub channels. XmlNodeRef rootSentenceNode = 0; for (int i = 0; i < xmlNode->getChildCount(); i++) { XmlNodeRef childNode = xmlNode->getChild(i); if (childNode->isTag("Channel")) { if (flags & SFLAG_ANIMATION) { SerializeChannelLoad(data, NULL, childNode); } } else if (childNode->isTag("Sentence")) { rootSentenceNode = childNode; } else if (childNode->isTag("SoundEntry")) { if (flags & SFLAG_SOUND_ENTRIES) { int soundEntry = int(data.m_soundEntries.size()); data.m_soundEntries.resize(data.m_soundEntries.size() + 1); data.m_soundEntries[soundEntry].m_sound = childNode->getAttr("Sound"); childNode->getAttr("StartTime", data.m_soundEntries[soundEntry].m_startTime); XmlNodeRef nodeSentence = childNode->findChild("Sentence"); if (nodeSentence != 0 && data.m_soundEntries[soundEntry].m_pSentence != NULL) { data.m_soundEntries[soundEntry].m_pSentence->Serialize(nodeSentence, true); } } } else if (childNode->isTag("SkeletonAnimationEntry")) { if (flags & SFLAG_ANIMATION) { int skeletonAnimationEntry = int(data.m_skeletonAnimationEntries.size()); data.m_skeletonAnimationEntries.resize(data.m_skeletonAnimationEntries.size() + 1); const char* szAnimationName = childNode->getAttr("AnimationName"); data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_animationName = (szAnimationName ? szAnimationName : ""); childNode->getAttr("StartTime", data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_startTime); } } else if (childNode->isTag("CameraPathPositions")) { if (flags & SFLAG_CAMERA_PATH) { data.m_cameraPathPosition.SerializeSpline(childNode, true); } } else if (childNode->isTag("CameraPathOrientations")) { if (flags & SFLAG_CAMERA_PATH) { data.m_cameraPathOrientation.SerializeSpline(childNode, true); } } else if (childNode->isTag("CameraPathFOV")) { if (flags & SFLAG_CAMERA_PATH) { data.m_cameraPathFOV.SerializeSpline(childNode, true); } } } // Old sequences have a single sound entry that is serialized in the root node. if (flags & SFLAG_SOUND_ENTRIES) { if (xmlNode->haveAttr("Sound") || rootSentenceNode) { int soundEntry = int(data.m_soundEntries.size()); data.m_soundEntries.resize(data.m_soundEntries.size() + 1); data.m_soundEntries[soundEntry].m_sound = xmlNode->getAttr("Sound"); data.m_soundEntries[soundEntry].m_startTime = 0.0f; if (rootSentenceNode != 0 && data.m_soundEntries[soundEntry].m_pSentence != NULL) { data.m_soundEntries[soundEntry].m_pSentence->Serialize(rootSentenceNode, true); } } } // Old sequences have a single skeleton animation entry that is serialized in the root node. if (flags & SFLAG_ANIMATION) { if (xmlNode->haveAttr("SkeletonAnimation")) { int skeletonAnimationEntry = int(data.m_skeletonAnimationEntries.size()); data.m_skeletonAnimationEntries.resize(data.m_skeletonAnimationEntries.size() + 1); data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_animationName = xmlNode->getAttr("SkeletonAnimation"); xmlNode->getAttr("SkeletonAnimationStart", data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_startTime); } } GenerateProceduralChannels(data); } void CFacialAnimSequence::SerializeChannelLoad(Data& data, IFacialAnimChannel* pChannel, XmlNodeRef& node) { data.m_nValidateID++; IFacialAnimChannel* pParentChannel = pChannel; CFacialAnimation* pFaceAnim = g_pCharacterManager->GetFacialAnimation(); if (!pFaceAnim) { return; } int flags = 0; node->getAttr("Flags", flags); const char* sName = node->getAttr("Name"); if (flags & IFacialAnimChannel::FLAG_GROUP) { // This is group. pChannel = data.CreateChannelGroup(); pChannel->SetFlags(flags); pChannel->SetIdentifier(pFaceAnim->CreateIdentifierHandle(sName)); pChannel->SetParent(pParentChannel); // Load all sub channels. for (int i = 0; i < node->getChildCount(); i++) { XmlNodeRef childNode = node->getChild(i); if (childNode->isTag("Channel")) { SerializeChannelLoad(data, pChannel, childNode); } } } else { // This is a normal effector. pChannel = data.CreateChannel(); pChannel->SetFlags(flags); pChannel->SetIdentifier(pFaceAnim->CreateIdentifierHandle(sName)); pChannel->SetParent(pParentChannel); // This is an effector. int splineCount = 0; for (int childIndex = 0, childCount = node->getChildCount(); childIndex < childCount; ++childIndex) { XmlNodeRef splineNode = node->getChild(childIndex); if (splineNode && _stricmp("Spline", splineNode->getTag()) == 0) { if (splineCount >= pChannel->GetInterpolatorCount()) { pChannel->AddInterpolator(); } ISplineInterpolator* pInterpolator = pChannel->GetInterpolator(splineCount); ++splineCount; pInterpolator->SerializeSpline(splineNode, true); } } } } void CFacialAnimSequence::SerializeChannelSave(IFacialAnimChannel* pChannel, XmlNodeRef& node) { #ifdef FACE_STORE_ASSET_VALUES node->setAttr("Flags", pChannel->GetFlags()); node->setAttr("Name", pChannel->GetName()); if (pChannel->GetFlags() & IFacialAnimChannel::FLAG_GROUP) { for (int j = 0; j < (int)m_data.m_channels.size(); j++) { IFacialAnimChannel* pSubChannel = m_data.m_channels[j]; if (pSubChannel->GetParent() == pChannel) { XmlNodeRef subChannelNode = node->newChild("Channel"); SerializeChannelSave(pSubChannel, subChannelNode); } } } else { for (int i = 0, count = pChannel->GetInterpolatorCount(); i < count; ++i) { XmlNodeRef splineNode = node->newChild("Spline"); pChannel->GetInterpolator(i)->SerializeSpline(splineNode, false); } } #endif } ////////////////////////////////////////////////////////////////////////// int CFacialAnimSequence::GetSkeletonAnimationEntryCount() { return int(m_data.m_skeletonAnimationEntries.size()); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::InsertSkeletonAnimationEntry(int index) { if (index < 0 || index > int(m_data.m_skeletonAnimationEntries.size())) { g_pILog->LogError("CFacialAnimSequence: Inserting skeleton animation entry at invalid location (Code bug)."); } else { m_data.m_skeletonAnimationEntries.insert(m_data.m_skeletonAnimationEntries.begin() + index, CFacialAnimSkeletalAnimationEntry()); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::DeleteSkeletonAnimationEntry(int index) { if (index < 0 || index >= int(m_data.m_skeletonAnimationEntries.size())) { g_pILog->LogError("CFacialAnimSequence: Deleting skeleton animation entry at invalid location (Code bug)."); } else { m_data.m_skeletonAnimationEntries.erase(m_data.m_skeletonAnimationEntries.begin() + index); } } ////////////////////////////////////////////////////////////////////////// IFacialAnimSkeletonAnimationEntry* CFacialAnimSequence::GetSkeletonAnimationEntry(int index) { if (index < 0 || index >= int(m_data.m_skeletonAnimationEntries.size())) { g_pILog->LogError("CFacialAnimSequence: Getting skeleton animation entry at invalid location (Code bug)."); return 0; } return &m_data.m_skeletonAnimationEntries[index]; } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::SetJoystickFile(const char* joystickFile) { m_data.m_joystickFile = joystickFile; } ////////////////////////////////////////////////////////////////////////// const char* CFacialAnimSequence::GetJoystickFile() const { return m_data.m_joystickFile.c_str(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::Serialize(XmlNodeRef& xmlNode, bool bLoading, ESerializationFlags flags) { if (bLoading) { SerializeLoad(m_data, xmlNode, flags); } else { ////////////////////////////////////////////////////////////////////////// xmlNode->removeAllChilds(); if (flags & SFLAG_ANIMATION) { for (int i = 0, end = (int)m_data.m_channels.size(); i < end; i++) { IFacialAnimChannel* pChannel = m_data.m_channels[i]; if (pChannel->GetParent()) // Only save top level channels. { continue; } XmlNodeRef channelNode = xmlNode->newChild("Channel"); SerializeChannelSave(pChannel, channelNode); } xmlNode->setAttr("Flags", m_data.m_nFlags); xmlNode->setAttr("StartTime", m_data.m_timeRange.start); xmlNode->setAttr("EndTime", m_data.m_timeRange.end); xmlNode->setAttr("Joysticks", m_data.m_joystickFile); } if (flags & SFLAG_SOUND_ENTRIES) { for (int soundEntry = 0, soundEntryCount = m_data.m_soundEntries.size(); soundEntry < soundEntryCount; ++soundEntry) { XmlNodeRef soundEntryNode(xmlNode->newChild("SoundEntry")); soundEntryNode->setAttr("Sound", m_data.m_soundEntries[soundEntry].m_sound); soundEntryNode->setAttr("StartTime", m_data.m_soundEntries[soundEntry].m_startTime); if (m_data.m_soundEntries[soundEntry].m_pSentence) { XmlNodeRef nodeRef(soundEntryNode->newChild("Sentence")); m_data.m_soundEntries[soundEntry].m_pSentence->Serialize(nodeRef, false); } } } if (flags & SFLAG_ANIMATION) { for (int skeletonAnimationEntry = 0, skeletonAnimationCount = m_data.m_skeletonAnimationEntries.size(); skeletonAnimationEntry < skeletonAnimationCount; ++skeletonAnimationEntry) { XmlNodeRef skeletonAnimationEntryNode(xmlNode->newChild("SkeletonAnimationEntry")); skeletonAnimationEntryNode->setAttr("AnimationName", m_data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_animationName); skeletonAnimationEntryNode->setAttr("StartTime", m_data.m_skeletonAnimationEntries[skeletonAnimationEntry].m_startTime); } } if (flags & SFLAG_CAMERA_PATH) { XmlNodeRef cameraPathPositionsNode(xmlNode->newChild("CameraPathPositions")); m_data.m_cameraPathPosition.SerializeSpline(cameraPathPositionsNode, false); XmlNodeRef cameraPathOrientationsNode(xmlNode->newChild("CameraPathOrientations")); m_data.m_cameraPathOrientation.SerializeSpline(cameraPathOrientationsNode, false); XmlNodeRef cameraPathFOVNode(xmlNode->newChild("CameraPathFOV")); m_data.m_cameraPathFOV.SerializeSpline(cameraPathFOVNode, false); } } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::MergeSequence(IFacialAnimSequence* pMergeSequence, const Functor1wRet<const char*, MergeCollisionAction>& collisionStrategy) { #ifdef FACE_STORE_ASSET_VALUES if (this == pMergeSequence) { CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, "Attempting to merge sequence with itself - aborting."); return; } if (!pMergeSequence) { CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, "No merge sequence supplied (NULL) - aborting."); return; } PREFAST_ASSUME(pMergeSequence); // wouldve returned if was NULL std::vector<string> originalFullPaths, mergeFullPaths; std::vector<string>* fullPathsList[2] = {&originalFullPaths, &mergeFullPaths}; Channels* channelsList[2] = {&m_data.m_channels, &((CFacialAnimSequence*)pMergeSequence)->m_data.m_channels}; for (int instance = 0; instance < 2; ++instance) { std::vector<string>& fullPaths = *fullPathsList[instance]; Channels& channels = *channelsList[instance]; fullPaths.resize(channels.size()); for (size_t i = 0, end = fullPaths.size(); i < end; ++i) { fullPaths[i] = channels[i]->GetName(); for (IFacialAnimChannel* pChannel = channels[i]->GetParent(); pChannel; pChannel = pChannel->GetParent()) { fullPaths[i] = string().Format("%s@:@%s", pChannel->GetName(), fullPaths[i].c_str()); } } } typedef VectorMap<string, IFacialAnimChannel*> NameChannelMap; // Merge in the channels. { NameChannelMap nameChannelMap; { NameChannelMap::container_type nameChannelMapEntries; nameChannelMapEntries.reserve(originalFullPaths.size()); for (size_t i = 0, end = originalFullPaths.size(); i < end; ++i) { nameChannelMapEntries.push_back(std::make_pair(originalFullPaths[i], m_data.m_channels[i])); } nameChannelMap.SwapElementsWithVector(nameChannelMapEntries); } for (int channelIndex = 0; channelIndex < pMergeSequence->GetChannelCount(); ++channelIndex) { IFacialAnimChannel* pMergeChannel = pMergeSequence->GetChannel(channelIndex); NameChannelMap::iterator itNameEntry = nameChannelMap.find(mergeFullPaths[channelIndex]); IFacialAnimChannel* pTargetChannel = 0; bool copyChannel = true; if (itNameEntry == nameChannelMap.end()) { pTargetChannel = CreateChannel(); originalFullPaths.push_back(mergeFullPaths[channelIndex]); } else { pTargetChannel = (*itNameEntry).second; if (!pTargetChannel->IsGroup()) { copyChannel = collisionStrategy(pMergeChannel->GetName()) == MergeCollisionActionOverwrite; } } if (pTargetChannel && copyChannel) { pTargetChannel->SetName(pMergeChannel->GetName()); pTargetChannel->SetFlags(pMergeChannel->GetFlags()); pTargetChannel->SetEffector(pMergeChannel->GetEffector()); ISplineInterpolator* pMergeSpline = pMergeChannel->GetLastInterpolator(); ISplineInterpolator* pTargetSpline = pTargetChannel->GetLastInterpolator(); if (pMergeSpline && pTargetSpline) { while (pTargetSpline->GetKeyCount()) { pTargetSpline->RemoveKey(0); } for (int key = 0; key < pMergeSpline->GetKeyCount(); ++key) { ISplineInterpolator::ValueType value; pMergeSpline->GetKeyValue(key, value); int originalKeyIndex = pTargetSpline->InsertKey(FacialEditorSnapTimeToFrame(pMergeSpline->GetKeyTime(key)), value); pTargetSpline->SetKeyFlags(originalKeyIndex, pMergeSpline->GetKeyFlags(key)); ISplineInterpolator::ValueType tin, tout; pMergeSpline->GetKeyTangents(key, tin, tout); pTargetSpline->SetKeyTangents(originalKeyIndex, tin, tout); } } } } } // Update the parents of the channels. { NameChannelMap nameChannelMap; { NameChannelMap::container_type nameChannelMapEntries; nameChannelMapEntries.reserve(originalFullPaths.size()); for (size_t i = 0; i < originalFullPaths.size(); ++i) { nameChannelMapEntries.push_back(std::make_pair(originalFullPaths[i], m_data.m_channels[i])); } nameChannelMap.SwapElementsWithVector(nameChannelMapEntries); } typedef VectorMap<IFacialAnimChannel*, int> ChannelIndexMap; ChannelIndexMap channelIndexMap; { ChannelIndexMap::container_type channelIndexMapEntries; channelIndexMapEntries.reserve(pMergeSequence->GetChannelCount()); for (int i = 0; i < pMergeSequence->GetChannelCount(); ++i) { channelIndexMapEntries.push_back(std::make_pair(pMergeSequence->GetChannel(i), i)); } channelIndexMap.SwapElementsWithVector(channelIndexMapEntries); } for (int channelIndex = 0; channelIndex < pMergeSequence->GetChannelCount(); ++channelIndex) { IFacialAnimChannel* pMergeChannel = pMergeSequence->GetChannel(channelIndex); NameChannelMap::iterator itNameEntry = nameChannelMap.find(mergeFullPaths[channelIndex]); IFacialAnimChannel* pTargetChannel = 0; if (itNameEntry != nameChannelMap.end()) { pTargetChannel = (*itNameEntry).second; } ChannelIndexMap::iterator itParentIndexEntry = channelIndexMap.end(); if (pMergeChannel->GetParent()) { itParentIndexEntry = channelIndexMap.find(pMergeChannel->GetParent()); } NameChannelMap::iterator itParentEntry = nameChannelMap.end(); if (itParentIndexEntry != channelIndexMap.end()) { itParentEntry = nameChannelMap.find(mergeFullPaths[(*itParentIndexEntry).second]); } IFacialAnimChannel* pTargetParent = 0; if (itParentEntry != nameChannelMap.end()) { pTargetParent = (*itParentEntry).second; } if (pTargetChannel) { pTargetChannel->SetParent(pTargetParent); } } } GenerateProceduralChannels(m_data); #else CryFatalError("Not available on this platform"); #endif } void CFacialAnimSequence::StreamAsyncOnComplete(IReadStream* pStream, unsigned nError) { if (pStream->IsError()) { return; } const char* buf = (const char*) pStream->GetBuffer(); uint32 lengthBuf = pStream->GetBytesRead(); XmlNodeRef root = g_pISystem->LoadXmlFromBuffer(buf, lengthBuf, false); if (!root) { // Used as a signal to the MT callback that an error occured SAFE_DELETE(m_pStreamingData); return; } unsigned flags = (gEnv->IsEditor() ? IFacialAnimSequence::SFLAG_ALL : IFacialAnimSequence::SFLAG_ALL & ~IFacialAnimSequence::SFLAG_CAMERA_PATH); SerializeLoad(*m_pStreamingData, root, (ESerializationFlags)flags); pStream->FreeTemporaryMemory(); } void CFacialAnimSequence::StreamOnComplete (IReadStream* pStream, unsigned nError) { using std::swap; FUNCTION_PROFILER(GetISystem(), PROFILE_ANIMATION); const char* file = pStream->GetName(); if (!m_pStreamingData) { gEnv->pLog->LogError(file, "Failed to load facial sequence"); m_pFaceAnim->OnSequenceStreamed(this, false); return; } if (pStream->IsError()) { gEnv->pLog->LogError(file, "Failed to stream facial sequence (%s)", file); m_pFaceAnim->OnSequenceStreamed(this, false); SAFE_DELETE(m_pStreamingData); return; } swap(*m_pStreamingData, m_data); m_pFaceAnim->OnSequenceStreamed(this, true); m_pStream = NULL; SAFE_DELETE(m_pStreamingData); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::UpdateProceduralChannels() { UpdateSoundEntriesValidateID(); if (m_data.m_nProceduralChannelsValidateID != m_data.m_nSoundEntriesValidateID) { GenerateProceduralChannels(m_data); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::GenerateProceduralChannels(Data& data) { CProceduralChannel* pUpDownChannel = data.m_proceduralChannels.GetChannel(CProceduralChannelSet::ChannelTypeHeadUpDown); while (pUpDownChannel->GetInterpolator()->GetKeyCount()) { pUpDownChannel->GetInterpolator()->RemoveKey(0); } CProceduralChannel* pLeftRightChannel = data.m_proceduralChannels.GetChannel(CProceduralChannelSet::ChannelTypeHeadRightLeft); while (pLeftRightChannel->GetInterpolator()->GetKeyCount()) { pLeftRightChannel->GetInterpolator()->RemoveKey(0); } pLeftRightChannel->GetInterpolator()->InsertKeyFloat(0, 0); pUpDownChannel->GetInterpolator()->InsertKeyFloat(0, 0); for (int soundEntry = 0, soundEntryCount = data.m_soundEntries.size(); soundEntry < soundEntryCount; ++soundEntry) { float fLeftRight = 0.0f; float fUpDown = 0.0f; float fLeftRightMax = 0.10f; float fUpDownMax = 0.12f; float fLeftRightDecayRate = 0.7f; float fUpDownDecayRate = 0.8f; float fLeftRightJumpMax = 0.10f; float fUpDownJumpMax = 0.15f; float fOldTime = data.m_soundEntries[soundEntry].m_startTime; float fTimeDeltaThreshold = 0.4f; for (int wordIndex = 0; wordIndex < data.m_soundEntries[soundEntry].m_pSentence->GetWordCount(); ++wordIndex) { IFacialSentence::Word lastWord; if (wordIndex > 0) { data.m_soundEntries[soundEntry].m_pSentence->GetWord(wordIndex - 1, lastWord); } IFacialSentence::Word word; data.m_soundEntries[soundEntry].m_pSentence->GetWord(wordIndex, word); float fTime = float(word.startTime) / 1000 + data.m_soundEntries[soundEntry].m_startTime; float fTimeDelta = fTime - fOldTime; float fRandLeftRight = cry_random(-1.0f, 1.0f); fRandLeftRight = sinf(fRandLeftRight * 3.14159f); float fJumpLeftRight = fRandLeftRight * fLeftRightJumpMax * fTimeDelta; fLeftRight += fJumpLeftRight; if (fLeftRight < -fLeftRightMax) { fLeftRight = -fLeftRightMax; } if (fLeftRight > fLeftRightMax) { fLeftRight = fLeftRightMax; } fLeftRight *= fLeftRightDecayRate; pLeftRightChannel->GetInterpolator()->InsertKeyFloat(float(word.startTime) / 1000, fLeftRight); if (fTimeDelta > fTimeDeltaThreshold && wordIndex > 0) { pLeftRightChannel->GetInterpolator()->InsertKeyFloat(float(lastWord.endTime) / 1000 + data.m_soundEntries[soundEntry].m_startTime, fLeftRight); } float fRandUpDown = cry_random(-1.0f, 1.0f); fRandUpDown = sinf(fRandUpDown * 3.14159f); float fJumpUpDown = fRandUpDown * fUpDownJumpMax * fTimeDelta; fUpDown += fJumpUpDown; if (fUpDown < -fUpDownMax) { fUpDown = -fUpDownMax; } if (fUpDown > fUpDownMax) { fUpDown = fUpDownMax; } fUpDown *= fUpDownDecayRate; pUpDownChannel->GetInterpolator()->InsertKeyFloat(float(word.startTime) / 1000 + data.m_soundEntries[soundEntry].m_startTime, fUpDown); if (fTimeDelta > fTimeDeltaThreshold && wordIndex > 0) { pUpDownChannel->GetInterpolator()->InsertKeyFloat(float(lastWord.endTime) / 1000 + data.m_soundEntries[soundEntry].m_startTime, fUpDown); } fOldTime = fTime; } } data.m_nProceduralChannelsValidateID = data.m_nSoundEntriesValidateID; } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequence::UpdateSoundEntriesValidateID() { for (int i = 0, count = m_data.m_soundEntries.size(); i < count; ++i) { if (m_data.m_soundEntries[i].IsSentenceInvalid()) { m_data.m_soundEntries[i].ValidateSentence(); ++m_data.m_nSoundEntriesValidateID; } } } void CFacialAnimSequence::GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); pSizer->AddObject(m_name); pSizer->AddObject(m_data.m_joystickFile); pSizer->AddObject(m_data.m_skeletonAnimationEntries); pSizer->AddObject(m_data.m_channels); pSizer->AddObject(m_data.m_soundEntries); pSizer->AddObject(m_data.m_proceduralChannels); pSizer->AddObject(m_data.m_cameraPathPosition); pSizer->AddObject(m_data.m_cameraPathFOV); } ////////////////////////////////////////////////////////////////////////// // CFacialAnimSoundEntry ////////////////////////////////////////////////////////////////////////// CFacialAnimSoundEntry::CFacialAnimSoundEntry() : m_pSentence(new CFacialSentence) { m_startTime = 0.0f; m_nSentenceValidateID = m_pSentence->GetValidateID(); } void CFacialAnimSoundEntry::SetSoundFile(const char* sSoundFile) { m_sound = sSoundFile; } const char* CFacialAnimSoundEntry::GetSoundFile() { return m_sound.c_str(); } float CFacialAnimSoundEntry::GetStartTime() { return m_startTime; } void CFacialAnimSoundEntry::SetStartTime(float time) { m_startTime = time; } void CFacialAnimSoundEntry::ValidateSentence() { m_nSentenceValidateID = m_pSentence->GetValidateID(); } bool CFacialAnimSoundEntry::IsSentenceInvalid() { return m_nSentenceValidateID != m_pSentence->GetValidateID(); } ////////////////////////////////////////////////////////////////////////// // CFacialAnimSkeletalAnimationEntry ////////////////////////////////////////////////////////////////////////// CFacialAnimSkeletalAnimationEntry::CFacialAnimSkeletalAnimationEntry() : m_startTime(0.0f) , m_endTime(1.0f) { } void CFacialAnimSkeletalAnimationEntry::SetName(const char* skeletonAnimationFile) { m_animationName = skeletonAnimationFile; } const char* CFacialAnimSkeletalAnimationEntry::GetName() const { return m_animationName.c_str(); } void CFacialAnimSkeletalAnimationEntry::SetStartTime(float time) { m_startTime = time; } float CFacialAnimSkeletalAnimationEntry::GetStartTime() const { return m_startTime; } void CFacialAnimSkeletalAnimationEntry::SetEndTime(float time) { m_endTime = time; } float CFacialAnimSkeletalAnimationEntry::GetEndTime() const { return m_endTime; } ////////////////////////////////////////////////////////////////////////// // CFacialAnimSequenceInstance ////////////////////////////////////////////////////////////////////////// class EffectorChannelPair { public: EffectorChannelPair(IFacialEffector* pEffector, int channelIndex) : pEffector(pEffector) , channelIndex(channelIndex) {} bool operator<(const EffectorChannelPair& other) const {return pEffector < other.pEffector; } bool operator>(const EffectorChannelPair& other) const {return pEffector > other.pEffector; } bool operator!=(const EffectorChannelPair& other) const {return pEffector != other.pEffector; } bool operator==(const EffectorChannelPair& other) const {return pEffector == other.pEffector; } IFacialEffector* pEffector; int channelIndex; }; void CFacialAnimSequenceInstance::BindChannels(CFacialAnimationContext* pContext, CFacialAnimSequence* pSequence) { bool bFoundCategoryBalance = false; UnbindChannels(); m_balanceChannelStateIndices.clear(); m_balanceChannelStateIndices.reserve(pContext->GetInstance()->GetState()->GetNumWeights()); uint32 numChannels = pSequence->GetChannelCount(); m_pAnimContext = pContext; CFacialInstance* pInstance = pContext->GetInstance(); m_channels.resize(numChannels); for (uint32 i = 0; i < numChannels; i++) { m_channels[i].nBalanceChannelListIndex = -1; } int rootBalanceChannelEntryIndex = -1; for (uint32 i = 0; i < numChannels; i++) { CFacialAnimChannel* pSeqChannel = (CFacialAnimChannel*)pSequence->GetChannel(i); ChannelInfo& chinfo = m_channels[i]; chinfo.nChannelId = INVALID_CHANNEL_ID; // MichaelS - If this channel is a balance controller, add it to the list of balance controllers. if (pSeqChannel->GetFlags() & IFacialAnimChannel::FLAG_BALANCE) { size_t balanceChannelEntryIndex = m_balanceChannelEntries.size(); m_balanceChannelEntries.resize(balanceChannelEntryIndex + 1); BalanceChannelEntry& entry = m_balanceChannelEntries[balanceChannelEntryIndex]; entry.nChannelIndex = i; entry.nMorphIndexStartIndex = 0; entry.nMorphIndexCount = 0; // The controller could either be one that applies to a certain category (ie folder) of expressions, // or it could be one that applies to a set of expressions in the sequence. If it is the latter, // then we need to associate the channel with its parent, so that it can be propagated to other channels // in the sequence. if ((pSeqChannel->GetFlags() & IFacialAnimChannel::FLAG_CATEGORY_BALANCE) == 0) { // Associate the channel with the parent. CFacialAnimChannel* pParentChannel = (CFacialAnimChannel*)pSeqChannel->GetParent(); if (pParentChannel) { ChannelInfo& parentInfo = m_channels[pParentChannel->GetInstanceChannelId()]; parentInfo.nBalanceChannelListIndex = balanceChannelEntryIndex; } else { rootBalanceChannelEntryIndex = balanceChannelEntryIndex; } } else { bFoundCategoryBalance = true; } } chinfo.bUse = false; if (!pSeqChannel->IsGroup() && (pSeqChannel->GetFlags() & (IFacialAnimChannel::FLAG_PHONEME_STRENGTH | IFacialAnimChannel::FLAG_PROCEDURAL_STRENGTH)) == 0) { //chinfo.pEffector = pSeqChannel->GetEffectorPtr(); chinfo.pEffector = (CFacialEffector*)pInstance->FindEffector(pSeqChannel->GetEffectorIdentifier()); if (chinfo.pEffector && !(pSeqChannel->GetFlags() & IFacialAnimChannel::FLAG_BALANCE)) { chinfo.bUse = true; // Make a channel in animation context. SFacialEffectorChannel effch; effch.bNotRemovable = true; // This channel cannot be removed by anim context. effch.status = SFacialEffectorChannel::STATUS_ONE; effch.pEffector = chinfo.pEffector; chinfo.nChannelId = m_pAnimContext->StartChannel(effch); chinfo.bUse = true; } } } // MichaelS - Propagate balance channels from parents to children. for (uint32 i = 0; i < numChannels; ++i) { CFacialAnimChannel* pSeqChannel = (CFacialAnimChannel*)pSequence->GetChannel(i); ChannelInfo& chinfo = m_channels[i]; if (chinfo.nBalanceChannelListIndex == -1) { CFacialAnimChannel* pParentChannel = (CFacialAnimChannel*)pSeqChannel->GetParent(); int nBalanceChannelListIndex = rootBalanceChannelEntryIndex; while (pParentChannel) { CRY_ASSERT(pParentChannel->GetInstanceChannelId() >= 0 && pParentChannel->GetInstanceChannelId() < int(m_channels.size())); ChannelInfo& parentInfo = m_channels[pParentChannel->GetInstanceChannelId()]; if (parentInfo.nBalanceChannelListIndex != -1) { nBalanceChannelListIndex = parentInfo.nBalanceChannelListIndex; break; } pParentChannel = (CFacialAnimChannel*)pParentChannel->GetParent(); } chinfo.nBalanceChannelListIndex = nBalanceChannelListIndex; } } // MichaelS - Apply category-based balance controls. if (bFoundCategoryBalance) { typedef std::vector<EffectorChannelPair> EffectorMultiMap; EffectorMultiMap effectorChannelMultiMap; effectorChannelMultiMap.reserve(m_channels.size()); for (int channelIndex = 0, end = int(m_channels.size()); channelIndex < end; ++channelIndex) { effectorChannelMultiMap.push_back(EffectorChannelPair(m_channels[channelIndex].pEffector, channelIndex)); } std::sort(effectorChannelMultiMap.begin(), effectorChannelMultiMap.end()); for (int balanceChannelIndex = 0; balanceChannelIndex < int(m_balanceChannelEntries.size()); ++balanceChannelIndex) { // Currently only category balance controls have an effector. if (m_channels[m_balanceChannelEntries[balanceChannelIndex].nChannelIndex].pEffector) { BalanceChannelEntry& entry = m_balanceChannelEntries[balanceChannelIndex]; entry.nMorphIndexStartIndex = int(m_balanceChannelStateIndices.size()); entry.nMorphIndexCount = 0; class Recurser { public: Recurser(EffectorMultiMap& effectorChannelMultiMap, Channels& channels, int balanceChannelIndex, std::vector<int>& balanceChannelStateIndices) : effectorChannelMultiMap(effectorChannelMultiMap) , channels(channels) , balanceChannelIndex(balanceChannelIndex) , balanceChannelStateIndices(balanceChannelStateIndices) { } void operator()(IFacialEffector* pEffector) { std::pair<EffectorMultiMap::iterator, EffectorMultiMap::iterator> range = std::equal_range( effectorChannelMultiMap.begin(), effectorChannelMultiMap.end(), EffectorChannelPair(pEffector, 0)); for (EffectorMultiMap::iterator itPair = range.first; itPair != range.second; ++itPair) { if (channels[(*itPair).channelIndex].nBalanceChannelListIndex == -1) { channels[(*itPair).channelIndex].nBalanceChannelListIndex = balanceChannelIndex; } } if (pEffector->GetIndexInState() >= 0) { balanceChannelStateIndices.push_back(pEffector->GetIndexInState()); } for (int subEffectorIndex = 0; subEffectorIndex < pEffector->GetSubEffectorCount(); ++subEffectorIndex) { (*this)(pEffector->GetSubEffector(subEffectorIndex)); } } private: EffectorMultiMap& effectorChannelMultiMap; Channels& channels; int balanceChannelIndex; std::vector<int>& balanceChannelStateIndices; }; Recurser(effectorChannelMultiMap, m_channels, balanceChannelIndex, m_balanceChannelStateIndices)( m_channels[m_balanceChannelEntries[balanceChannelIndex].nChannelIndex].pEffector); entry.nMorphIndexCount = int(m_balanceChannelStateIndices.size()) - entry.nMorphIndexStartIndex; } } } BindProceduralChannels(pContext, pSequence); m_nValidateID = pSequence->GetValidateId(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequenceInstance::BindProceduralChannels(CFacialAnimationContext* pContext, CFacialAnimSequence* pSequence) { UnbindProceduralChannels(); CFacialInstance* pInstance = pContext->GetInstance(); m_proceduralChannels.resize(CProceduralChannelSet::ChannelType_count); for (int i = 0; i < CProceduralChannelSet::ChannelType_count; i++) { CProceduralChannel* pSeqChannel = pSequence->GetProceduralChannelSet().GetChannel(static_cast<CProceduralChannelSet::ChannelType>(i)); ChannelInfo& chinfo = m_proceduralChannels[i]; chinfo.bUse = false; chinfo.nChannelId = INVALID_CHANNEL_ID; chinfo.pEffector = (CFacialEffector*)pInstance->FindEffector(pSeqChannel->GetEffectorIdentifier()); chinfo.nBalanceChannelListIndex = -1; if (chinfo.pEffector && pSeqChannel->GetInterpolator()->GetKeyCount()) { chinfo.bUse = true; // Make a channel in animation context. SFacialEffectorChannel effch; effch.bNotRemovable = true; // This channel cannot be removed by anim context. effch.status = SFacialEffectorChannel::STATUS_ONE; effch.pEffector = chinfo.pEffector; chinfo.nChannelId = m_pAnimContext->StartChannel(effch); } } m_nValidateID = pSequence->GetValidateId(); } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequenceInstance::UnbindChannels() { if (!m_pAnimContext) { return; } int numChannels = (int)m_channels.size(); for (int i = 0; i < numChannels; i++) { ChannelInfo& chinfo = m_channels[i]; if (chinfo.nChannelId != INVALID_CHANNEL_ID) { m_pAnimContext->RemoveChannel(chinfo.nChannelId); } } if (m_nCurrentPhonemeChannelId != -1) { m_pAnimContext->RemoveChannel(m_nCurrentPhonemeChannelId); } m_channels.clear(); m_balanceChannelEntries.clear(); CCharInstance* pMasterCharacter = m_pAnimContext->GetInstance()->GetMasterCharacter(); if (pMasterCharacter) { //pMasterCharacter->m_Morphing.m_fMorphVertexDrag = 1.0f; UnbindProceduralChannels(); } } ////////////////////////////////////////////////////////////////////////// void CFacialAnimSequenceInstance::UnbindProceduralChannels() { if (!m_pAnimContext) { return; } int numChannels = (int)m_proceduralChannels.size(); for (int i = 0; i < numChannels; i++) { ChannelInfo& chinfo = m_proceduralChannels[i]; if (chinfo.nChannelId != INVALID_CHANNEL_ID) { m_pAnimContext->RemoveChannel(chinfo.nChannelId); } } m_proceduralChannels.clear(); } void CFacialAnimSequenceInstance::GetMemoryUsage(ICrySizer* pSizer) const { pSizer->AddObject(m_channels); pSizer->AddObject(m_proceduralChannels); pSizer->AddObject(m_balanceChannelStateIndices); pSizer->AddObject(m_balanceChannelEntries); pSizer->AddObject(m_nValidateID); pSizer->AddObject(m_pAnimContext); pSizer->AddObject(m_nCurrentPhoneme); pSizer->AddObject(m_nCurrentPhonemeChannelId); } ////////////////////////////////////////////////////////////////////////// CProceduralChannelSet::CProceduralChannelSet() { CFacialAnimation* pFaceAnim = g_pCharacterManager->GetFacialAnimation(); m_channels[ChannelTypeHeadUpDown].SetEffectorIdentifier(pFaceAnim->CreateIdentifierHandle("Neck_up_down")); m_channels[ChannelTypeHeadRightLeft].SetEffectorIdentifier(pFaceAnim->CreateIdentifierHandle("Neck_left_right")); } ////////////////////////////////////////////////////////////////////////// void CFacialCameraPathPositionInterpolator::SerializeSpline(XmlNodeRef& node, bool bLoading) { if (bLoading) { string keystr = node->getAttr("Keys"); resize(0); int curPos = 0; string key = keystr.Tokenize(",", curPos); while (!key.empty()) { float time; Vec3 v; int flags = 0; PREFAST_SUPPRESS_WARNING(6031) azsscanf(key, "%g:%g/%g/%g:%d", &time, &v.x, &v.y, &v.z, &flags); ValueType val; val[0] = v.x; val[1] = v.y; val[2] = v.z; int nKey = InsertKey(time, val); if (nKey >= 0) { SetKeyFlags(nKey, flags); } key = keystr.Tokenize(",", curPos); } ; } else { string keystr; string skey; for (int i = 0; i < num_keys(); i++) { skey.Format("%g:%g/%g/%g:%d,", key(i).time, key(i).value.x, key(i).value.y, key(i).value.z, key(i).flags); keystr += skey; } node->setAttr("Keys", keystr); } } ////////////////////////////////////////////////////////////////////////// void CFacialCameraPathOrientationInterpolator::SerializeSpline(XmlNodeRef& node, bool bLoading) { if (bLoading) { string keystr = node->getAttr("Keys"); resize(0); int curPos = 0; string key = keystr.Tokenize(",", curPos); while (!key.empty()) { float time; Quat v; int flags = 0; PREFAST_SUPPRESS_WARNING(6031) azsscanf(key, "%g:%g/%g/%g/%g:%d", &time, &v.v.x, &v.v.y, &v.v.z, &v.w, &flags); ValueType val; val[0] = v.v.x; val[1] = v.v.y; val[2] = v.v.z; val[3] = v.w; int nKey = InsertKey(time, val); if (nKey >= 0) { SetKeyFlags(nKey, flags); } key = keystr.Tokenize(",", curPos); } ; } else { string keystr; string skey; for (int i = 0; i < num_keys(); i++) { skey.Format("%g:%g/%g/%g/%g:%d,", key(i).time, key(i).value.v.x, key(i).value.v.y, key(i).value.v.z, key(i).value.w, key(i).flags); keystr += skey; } node->setAttr("Keys", keystr); } }