/* * 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 "SkeletonAnim.h" #include "ParametricSampler.h" #include "Model.h" #include "CharacterManager.h" #include "GlobalAnimationHeaderLMG.h" namespace VirtualExampleXML { void ReadFromXmlNode(const XmlNodeRef& xmlNode, VirtualExample1D& vOut) { CRY_ASSERT(xmlNode); xmlNode->getAttr("i0", vOut.i0); xmlNode->getAttr("i1", vOut.i1); xmlNode->getAttr("w0", vOut.w0); xmlNode->getAttr("w1", vOut.w1); } void ReadFromXmlNode(const XmlNodeRef& xmlNode, VirtualExample2D& vOut) { CRY_ASSERT(xmlNode); xmlNode->getAttr("i0", vOut.i0); xmlNode->getAttr("i1", vOut.i1); xmlNode->getAttr("i2", vOut.i2); xmlNode->getAttr("i3", vOut.i3); xmlNode->getAttr("w0", vOut.w0); xmlNode->getAttr("w1", vOut.w1); xmlNode->getAttr("w2", vOut.w2); xmlNode->getAttr("w3", vOut.w3); } void ReadFromXmlNode(const XmlNodeRef& xmlNode, VirtualExample3D& vOut) { CRY_ASSERT(xmlNode); xmlNode->getAttr("i0", vOut.i0); xmlNode->getAttr("i1", vOut.i1); xmlNode->getAttr("i2", vOut.i2); xmlNode->getAttr("i3", vOut.i3); xmlNode->getAttr("i4", vOut.i4); xmlNode->getAttr("i5", vOut.i5); xmlNode->getAttr("i6", vOut.i6); xmlNode->getAttr("i7", vOut.i7); xmlNode->getAttr("w0", vOut.w0); xmlNode->getAttr("w1", vOut.w1); xmlNode->getAttr("w2", vOut.w2); xmlNode->getAttr("w3", vOut.w3); xmlNode->getAttr("w4", vOut.w4); xmlNode->getAttr("w5", vOut.w5); xmlNode->getAttr("w6", vOut.w6); xmlNode->getAttr("w7", vOut.w7); } void WriteToXmlNode(const XmlNodeRef& xmlNode, const VirtualExample1D& vOut) { CRY_ASSERT(xmlNode); xmlNode->setAttr("i0", vOut.i0); xmlNode->setAttr("i1", vOut.i1); xmlNode->setAttr("w0", vOut.w0); xmlNode->setAttr("w1", vOut.w1); } void WriteToXmlNode(const XmlNodeRef& xmlNode, const VirtualExample2D& vOut) { CRY_ASSERT(xmlNode); xmlNode->setAttr("i0", vOut.i0); xmlNode->setAttr("i1", vOut.i1); xmlNode->setAttr("i2", vOut.i2); xmlNode->setAttr("i3", vOut.i3); xmlNode->setAttr("w0", vOut.w0); xmlNode->setAttr("w1", vOut.w1); xmlNode->setAttr("w2", vOut.w2); xmlNode->setAttr("w3", vOut.w3); } void WriteToXmlNode(const XmlNodeRef& xmlNode, const VirtualExample3D& vOut) { CRY_ASSERT(xmlNode); xmlNode->setAttr("i0", vOut.i0); xmlNode->setAttr("i1", vOut.i1); xmlNode->setAttr("i2", vOut.i2); xmlNode->setAttr("i3", vOut.i3); xmlNode->setAttr("i4", vOut.i4); xmlNode->setAttr("i5", vOut.i5); xmlNode->setAttr("i6", vOut.i6); xmlNode->setAttr("i7", vOut.i7); xmlNode->setAttr("w0", vOut.w0); xmlNode->setAttr("w1", vOut.w1); xmlNode->setAttr("w2", vOut.w2); xmlNode->setAttr("w3", vOut.w3); xmlNode->setAttr("w4", vOut.w4); xmlNode->setAttr("w5", vOut.w5); xmlNode->setAttr("w6", vOut.w6); xmlNode->setAttr("w7", vOut.w7); } template<typename TVirtualExample> bool ReadVGrid(const XmlNodeRef& xmlNode, const uint32 expectedVirtualExampleCount, DynArray<TVirtualExample>& virtualExampleGrid) { CRY_ASSERT(xmlNode); const uint32 virtualExampleCount = xmlNode->getChildCount(); if (virtualExampleCount != expectedVirtualExampleCount) { return false; } virtualExampleGrid.resize(virtualExampleCount); for (uint32 i = 0; i < virtualExampleCount; ++i) { XmlNodeRef xmlExampleNode = xmlNode->getChild(i); TVirtualExample& virtualExample = virtualExampleGrid[i]; ReadFromXmlNode(xmlExampleNode, virtualExample); } return true; } template<typename TVirtualExample> bool WriteVGrid(const XmlNodeRef& xmlNode, const DynArray<TVirtualExample>& virtualExampleGrid) { CRY_ASSERT(xmlNode); const uint32 virtualExampleCount = virtualExampleGrid.size(); for (uint32 i = 0; i < virtualExampleCount; ++i) { const TVirtualExample& virtualExample = virtualExampleGrid[i]; XmlNodeRef xmlExampleNode = xmlNode->createNode("VExample"); WriteToXmlNode(xmlExampleNode, virtualExample); xmlNode->addChild(xmlExampleNode); } return true; } bool ReadVGrid(const XmlNodeRef& xmlNode, GlobalAnimationHeaderLMG& gah) { CRY_ASSERT(xmlNode); if (gah.m_Dimensions == 1) { const uint32 expectedVirtualExampleCount = gah.m_DimPara[0].m_cells; return ReadVGrid(xmlNode, expectedVirtualExampleCount, gah.m_VirtualExampleGrid1D); } else if (gah.m_Dimensions == 2) { const uint32 expectedVirtualExampleCount = gah.m_DimPara[0].m_cells * gah.m_DimPara[1].m_cells; return ReadVGrid(xmlNode, expectedVirtualExampleCount, gah.m_VirtualExampleGrid2D); } else if (gah.m_Dimensions == 3) { const uint32 expectedVirtualExampleCount = gah.m_DimPara[0].m_cells * gah.m_DimPara[1].m_cells * gah.m_DimPara[2].m_cells; return ReadVGrid(xmlNode, expectedVirtualExampleCount, gah.m_VirtualExampleGrid3D); } return false; } bool WriteVGrid(const XmlNodeRef& xmlNode, const GlobalAnimationHeaderLMG& gah) { CRY_ASSERT(xmlNode); if (gah.m_Dimensions == 1) { return WriteVGrid(xmlNode, gah.m_VirtualExampleGrid1D); } else if (gah.m_Dimensions == 2) { return WriteVGrid(xmlNode, gah.m_VirtualExampleGrid2D); } else if (gah.m_Dimensions == 3) { return WriteVGrid(xmlNode, gah.m_VirtualExampleGrid3D); } return false; } } bool GlobalAnimationHeaderLMG::LoadAndParseXML(CAnimationSet* pAnimationSet, bool nForceReloading) { if (IsAssetLMG() == 0) { CryFatalError("CryAnimation: data mismatch"); } if (nForceReloading == 0) { uint32 numAnims = m_numExamples;//m_arrBSAnimations.size(); if (numAnims) { return true; //already loaded and initialized } } const char* pathnameLMG = GetFilePath(); const char* fileExt = PathUtil::GetExt(pathnameLMG); uint32 IsLMG = (_stricmp(fileExt, "lmg") == 0); if (IsLMG) { CryFatalError("CryAnimation: LMGs not supported any more: %s! please use only 'BSpace' and 'COMB' from now on", pathnameLMG); } XmlNodeRef root = g_pISystem->LoadXmlFromFile(pathnameLMG); return LoadFromXML(pAnimationSet, root); } bool GlobalAnimationHeaderLMG::LoadFromXML(CAnimationSet* pAnimationSet, XmlNodeRef root) { const char* pathnameLMG = GetFilePath(); const char* fileExt = PathUtil::GetExt(pathnameLMG); InvalidateAssetCreated(); InvalidateAssetLMG(); m_VEG_Flags &= ~CA_ERROR_REPORTED; m_numExamples = 0; m_Dimensions = 0; m_ExtractionParams = 0; m_Status.clear(); m_arrParameter.clear(); m_arrCombinedBlendSpaces.clear(); m_arrBSAnnotations.clear(); m_VirtualExampleGrid1D.clear(); m_VirtualExampleGrid2D.clear(); m_VirtualExampleGrid3D.clear(); m_DimPara[0].init(); m_DimPara[1].init(); m_DimPara[2].init(); m_DimPara[3].init(); m_ExtPara[0].init(); m_ExtPara[1].init(); m_ExtPara[2].init(); m_ExtPara[3].init(); if (root == 0) { int32 bPakPriority = gEnv->pCryPak->GetPakPriority(); bool bIsOnDisk = gEnv->pCryPak->IsFileExist(pathnameLMG, ICryPak::eFileLocation_OnDisk); bool bIsInPak = gEnv->pCryPak->IsFileExist(pathnameLMG, ICryPak::eFileLocation_InPak); if (bPakPriority == 0) { //try to load from disk first if (bIsOnDisk) { m_Status = "Error: Parametric-Group loaded from Disk, but it has an XML-Syntax Error"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (bIsInPak) { m_Status = "Error: Parametric-Group loaded from PAK, but it has an XML-Syntax Error"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } } else { //try to load from PAK first if (bIsInPak) { m_Status = "Error: Parametric-Group loaded from PAK, but it has an XML-Syntax Error"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (bIsOnDisk) { m_Status = "Error: Parametric-Group loaded from Disk, but it has an XML-Syntax Error"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } } OnAssetNotFound(); m_Status = "Error: Parametric-Group not found"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); //CryFatalError("CryAnimation: Parametric-Group not found: %s", pathnameLMG); return false; } //the asset for the ParaGroup exists and has no Syntax Error OnAssetCreated(); //--------------------------------------------------------- //--- parse and verify XML //--------------------------------------------------------- const char* XMLTAG = root->getTag(); if (strcmp(XMLTAG, "ParaGroup") && strcmp(XMLTAG, "CombinedBlendSpace")) { ((m_Status = "Error: The XMLTAG is '") += XMLTAG) += "'. It is expected to be 'ParaGroup' or 'CombinedBlendSpace'"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (strcmp(XMLTAG, "ParaGroup") == 0) { uint32 numPseudo = 0; uint32 numExamples = 0; uint32 numChilds = root->getChildCount(); for (uint32 c = 0; c < numChilds; c++) { XmlNodeRef nodeList = root->getChild(c); const char* ListTag = nodeList->getTag(); //load example-list if (strcmp(ListTag, "ExampleList") == 0) { numExamples = nodeList->getChildCount(); if (numExamples == 0) { m_Status = "Error: no examples in this ParaGroup."; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (numExamples >= MAX_LMG_ANIMS) { m_Status = "Error: too many examples in one ParaGroup. Only 40 are currently allowed! "; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } } //load pseudo example-list if (strcmp(ListTag, "ExamplePseudo") == 0) { numPseudo = nodeList->getChildCount(); } } //reserve real and pseudo parameters m_arrParameter.resize(numPseudo + numExamples); //---------------------------------------------------------------------- for (uint32 c = 0; c < numChilds; c++) { XmlNodeRef nodeList = root->getChild(c); const char* ListTag = nodeList->getTag(); //---------------------------------------------------------------------------------- //--- temporary helper flags to ensure compatibility with the old system --- //--- they will disappear sooner or later --- //---------------------------------------------------------------------------------- if (strcmp(ListTag, "THRESHOLD") == 0) { nodeList->getAttr("tz", m_fThreshold); //will go as soon as we have the Blend Nodes to combine VEGs continue; } if (strcmp(ListTag, "VEGPARAMS") == 0) { uint32 nFlags = 0; nodeList->getAttr("Idle2Move", nFlags); //will go as soon as CryMannegin is up and running if (nFlags) { m_VEG_Flags |= CA_VEG_I2M; //this is a case for CryMannequin } continue; } //----------------------------------------------------------- //--- define dimensions of the LMG --- //----------------------------------------------------------- if (strcmp(ListTag, "Dimensions") == 0) { m_Dimensions = nodeList->getChildCount(); if (m_Dimensions > 3) { m_Status = "Error: More then 3 dimensions per Blend-Space are not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } for (uint32 d = 0; d < m_Dimensions; d++) { XmlNodeRef nodeExample = nodeList->getChild(d); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Param") == 0) { //each dimension must have a parameter-name m_DimPara[d].m_strParaName = nodeExample->getAttr("name"); //check if the parameter-name is supported by the system uint32 supported = 0; if (m_DimPara[d].m_strParaName == "MoveSpeed") { m_DimPara[d].m_ParaID = eMotionParamID_TravelSpeed, supported = 1; } if (m_DimPara[d].m_strParaName == "TurnSpeed") { m_DimPara[d].m_ParaID = eMotionParamID_TurnSpeed, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelSlope") { m_DimPara[d].m_ParaID = eMotionParamID_TravelSlope, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelAngle") { m_DimPara[d].m_ParaID = eMotionParamID_TravelAngle, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelDist") { m_DimPara[d].m_ParaID = eMotionParamID_TravelDist, supported = 1; } if (m_DimPara[d].m_strParaName == "TurnAngle") { m_DimPara[d].m_ParaID = eMotionParamID_TurnAngle, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight, supported = 1; //this is just a custom "Blend-Node" } if (m_DimPara[d].m_strParaName == "BlendWeight2") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight2, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight3") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight3, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight4") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight4, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight5") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight5, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight6") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight6, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight7") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight7, supported = 1; } if (m_DimPara[d].m_strParaName == "StopLeg") { m_DimPara[d].m_ParaID = eMotionParamID_StopLeg, supported = 1; //I don't like this. It's a case for CryManequin } if (supported == 0) { ((m_Status = "Error: The parameter '") += m_DimPara[d].m_strParaName.c_str()) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } //define the scope of the blend-space for each dimension nodeExample->getAttr("min", m_DimPara[d].m_min); nodeExample->getAttr("max", m_DimPara[d].m_max); nodeExample->getAttr("cells", m_DimPara[d].m_cells); m_DimPara[d].m_cells = m_DimPara[d].m_cells < 3 ? 3 : m_DimPara[d].m_cells; nodeExample->getAttr("scale", m_DimPara[d].m_scale); //just for visual-debugging m_DimPara[d].m_scale = max(0.01f, m_DimPara[d].m_scale); //from which joint do we wnat to extract the parameters to initialize the patameter-space?? m_DimPara[d].m_strJointName = nodeExample->getAttr("JointName"); nodeExample->getAttr("skey", m_DimPara[d].m_skey); nodeExample->getAttr("ekey", m_DimPara[d].m_ekey); //special flags per-dimension m_DimPara[d].m_nDimensionFlags = 0; uint32 flag = 0; nodeExample->getAttr("locked", flag); if (flag) { m_DimPara[d].m_nDimensionFlags |= CA_Dim_LockedParameter; } } } continue; } //----------------------------------------------------------- //--- define the additioanl extraction parameters --- //----------------------------------------------------------- if (strcmp(ListTag, "AdditionalExtraction") == 0) { m_ExtractionParams = nodeList->getChildCount(); if (m_ExtractionParams > 4) { m_Status = "Error: More then 4 additional extraction parameters are not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } for (uint32 d = 0; d < m_ExtractionParams; d++) { XmlNodeRef nodeExample = nodeList->getChild(d); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Param") == 0) { //each dimension must have a parameter-name m_ExtPara[d].m_strParaName = nodeExample->getAttr("name"); //check if the parameter-name is supported by the system uint32 supported = 0; if (m_ExtPara[d].m_strParaName == "MoveSpeed") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelSpeed, supported = 1; } if (m_ExtPara[d].m_strParaName == "TurnSpeed") { m_ExtPara[d].m_ParaID = eMotionParamID_TurnSpeed, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelSlope") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelSlope, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelAngle") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelAngle, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelDist") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelDist, supported = 1; } if (m_ExtPara[d].m_strParaName == "TurnAngle") { m_ExtPara[d].m_ParaID = eMotionParamID_TurnAngle, supported = 1; } if (supported == 0) { ((m_Status = "Error: The parameter '") += m_DimPara[d].m_strParaName.c_str()) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } } } continue; } //----------------------------------------------------------- //load example-list //----------------------------------------------------------- if (strcmp(ListTag, "ExampleList") == 0) { m_numExamples = nodeList->getChildCount(); bool isAdditive = false; for (uint32 i = 0; i < m_numExamples; i++) { XmlNodeRef nodeExample = nodeList->getChild(i); const char* ExampleTAG = nodeExample->getTag(); if (strcmp(ExampleTAG, "Example")) { ((m_Status = "Error: The ExampleTAG for an example is '") += ExampleTAG) += "'. It is expected to be 'Example'"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (strcmp(ExampleTAG, "Example") == 0) { m_arrParameter[i].m_animName.SetName(nodeExample->getAttr("AName")); nodeExample->getAttr("PlaybackScale", m_arrParameter[i].m_fPlaybackScale); //Pre-initialized parameters should be an exception. Only use them if real extraction is impossible m_arrParameter[i].m_PreInitialized[0] = nodeExample->getAttr("SetPara0", m_arrParameter[i].m_Para.x); m_arrParameter[i].m_PreInitialized[1] = nodeExample->getAttr("SetPara1", m_arrParameter[i].m_Para.y); if (m_arrParameter[i].m_PreInitialized[1] && m_Dimensions < 2) { m_Status = "Error: SetPara1 is not allowed on BSpaces with less than 2 dimensions"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } m_arrParameter[i].m_PreInitialized[2] = nodeExample->getAttr("SetPara2", m_arrParameter[i].m_Para.z); if (m_arrParameter[i].m_PreInitialized[2] && m_Dimensions < 3) { m_Status = "Error: SetPara1 is not allowed on BSpaces with less than 3 dimensions"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } m_arrParameter[i].m_PreInitialized[3] = nodeExample->getAttr("SetPara3", m_arrParameter[i].m_Para.w); if (m_arrParameter[i].m_PreInitialized[3] && m_Dimensions < 4) { m_Status = "Error: SetPara1 is not allowed on BSpaces with less than 4 dimensions"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } nodeExample->getAttr("UseDirectlyForDeltaMotion0", m_arrParameter[i].m_UseDirectlyForDeltaMotion[0]); nodeExample->getAttr("UseDirectlyForDeltaMotion1", m_arrParameter[i].m_UseDirectlyForDeltaMotion[1]); nodeExample->getAttr("UseDirectlyForDeltaMotion2", m_arrParameter[i].m_UseDirectlyForDeltaMotion[2]); nodeExample->getAttr("UseDirectlyForDeltaMotion3", m_arrParameter[i].m_UseDirectlyForDeltaMotion[3]); m_arrParameter[i].i0 = i; m_arrParameter[i].w0 = 1.0f; //real examples always have a weight of 1.0f m_arrParameter[i].i1 = 0; m_arrParameter[i].w1 = 0.0f; //--------------------------------------------------- //--- error handling --- //--------------------------------------------------- const SCRCName& aName = m_arrParameter[i].m_animName; // const char* pDebugName2 = aName.GetName_DEBUG(); // int32 id2=pAnimationSet->GetAnimIDByName(pDebugName2); int32 id = pAnimationSet->GetAnimIDByCRC(aName.m_CRC32); if (id < 0) { //name not found in animation-list (chrparams file error) const char* pDebugName = aName.GetName_DEBUG(); ((m_Status = "Error: The animation '") += pDebugName) += "' is not in the chrparams file"; gEnv->pLog->LogError("The ParaGroup '%s' is invalid! The animation '%s' is not in the chrparams file. Model: %s", pathnameLMG, pDebugName, pAnimationSet->GetSkeletonFilePathDebug()); return false; } else { const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(id); if (pAnim->m_nAssetType != CAF_File) { gEnv->pLog->LogError("The ParaGroup '%s' is invalid! The the asset type for animation '%s' is wrong (expect CAF type). Model: %s", pathnameLMG, aName.GetName_DEBUG(), pAnimationSet->GetSkeletonFilePathDebug()); return false; } int32 gaid = pAnim->m_nGlobalAnimId; GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[gaid]; if (rCAF.IsAssetNotFound()) { ((m_Status = "Error: The asset for the animation '") += aName.GetName_DEBUG()) += "' does not exist"; gEnv->pLog->LogError("The ParaGroup '%s' is invalid! The the asset for animation '%s' does not exist. Model: %s", pathnameLMG, aName.GetName_DEBUG(), pAnimationSet->GetSkeletonFilePathDebug()); return false; } else { if (i == 0) { isAdditive = 0 != rCAF.IsAssetAdditive(); } else if (isAdditive != (0 != rCAF.IsAssetAdditive())) { gEnv->pLog->LogError("The ParaGroup '%s' is invalid! Animation asset '%s' is an %s, though other assets are %s. Skeleton: %s", pathnameLMG, aName.m_name.c_str(), isAdditive ? "override" : "additive", isAdditive ? "additive" : "override", pAnimationSet->GetSkeletonFilePathDebug()); return false; } } } } } continue; } //----------------------------------------------------------- //load pseudo example-list //----------------------------------------------------------- if (strcmp(ListTag, "ExamplePseudo") == 0) { uint32 num = nodeList->getChildCount(); for (uint32 i = 0; i < num; i++) { XmlNodeRef nodeExample = nodeList->getChild(i); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Pseudo") == 0) { uint32 i0 = -1; f32 w0 = 1.0f; uint32 i1 = -1; f32 w1 = 1.0f; nodeExample->getAttr("p0", i0); ANIM_ASSERT(i0 < numExamples); nodeExample->getAttr("p1", i1); ANIM_ASSERT(i1 < numExamples); nodeExample->getAttr("w0", w0); nodeExample->getAttr("w1", w1); f32 sum = w0 + w1; ANIM_ASSERT(fabsf(1.0f - sum) < 0.00001f); m_arrParameter[numExamples + i].i0 = i0; m_arrParameter[numExamples + i].w0 = w0; m_arrParameter[numExamples + i].i1 = i1; m_arrParameter[numExamples + i].w1 = w1; // m_arrParameter[numExamples+i].m_Para=m_arrParameter[i0].m_Para*w0 + m_arrParameter[i1].m_Para*w1; // Vec4 w4=m_arrParameter[numExamples+i].m_Para; } } continue; } //----------------------------------------------------------- //--- load blend-annotation-list -- //----------------------------------------------------------- if (strcmp(ListTag, "Blendable") == 0) { uint32 num = nodeList->getChildCount(); m_arrBSAnnotations.reserve(num); for (uint32 i = 0; i < num; i++) { XmlNodeRef nodeExample = nodeList->getChild(i); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Face") == 0) { BSBlendable face; uint32 res; uint32 numTotalExamples = numExamples + numPseudo; res = nodeExample->getAttr("p0", face.idx0); if (res) { EnsureValidFaceExampleIndex(face.idx0, numTotalExamples); face.num++; } res = nodeExample->getAttr("p1", face.idx1); if (res) { EnsureValidFaceExampleIndex(face.idx1, numTotalExamples); face.num++; } res = nodeExample->getAttr("p2", face.idx2); if (res) { EnsureValidFaceExampleIndex(face.idx2, numTotalExamples); face.num++; } res = nodeExample->getAttr("p3", face.idx3); if (res) { EnsureValidFaceExampleIndex(face.idx3, numTotalExamples); face.num++; } res = nodeExample->getAttr("p4", face.idx4); if (res) { EnsureValidFaceExampleIndex(face.idx4, numTotalExamples); face.num++; } res = nodeExample->getAttr("p5", face.idx5); if (res) { EnsureValidFaceExampleIndex(face.idx5, numTotalExamples); face.num++; } res = nodeExample->getAttr("p6", face.idx6); if (res) { EnsureValidFaceExampleIndex(face.idx6, numTotalExamples); face.num++; } res = nodeExample->getAttr("p7", face.idx7); if (res) { EnsureValidFaceExampleIndex(face.idx7, numTotalExamples); face.num++; } m_arrBSAnnotations.push_back(face); } } continue; } //----------------------------------------------------------- //--- load precomputed example grid -- //----------------------------------------------------------- if (strcmp(ListTag, "VGrid") == 0) { VirtualExampleXML::ReadVGrid(nodeList, *this); continue; } //----------------------------------------------------------- //-- joint mask //----------------------------------------------------------- if (strcmp(ListTag, "JointList") == 0) { uint32 num = nodeList->getChildCount(); m_jointList.resize(num); for (uint32 i = 0; i < num; ++i) { XmlNodeRef node = nodeList->getChild(i); const char* tag = node->getTag(); if (strcmp(tag, "Joint") != 0) { ((m_Status = "Error: The XML-Tag '") += tag) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return 0; } const char* name = node->getAttr("Name"); m_jointList[i] = CCrc32::Compute(name); } std::sort(m_jointList.begin(), m_jointList.end()); continue; } if (strcmp(ListTag, "MotionCombination") == 0) { gEnv->pLog->LogError("CryAnimation: <MotionCombination> element found in bspace '%s'. Those are not supported anymore. Please use dedicated animation layers to achieve the same result instead.", pathnameLMG); continue; } } } //Paragroup //---------------------------------------------------------------------- //---- check of this is a combined Blend-Space ----------- //---------------------------------------------------------------------- if (strcmp(XMLTAG, "CombinedBlendSpace") == 0) { uint32 numChilds = root->getChildCount(); for (uint32 c = 0; c < numChilds; c++) { XmlNodeRef nodeList = root->getChild(c); const char* ListTag = nodeList->getTag(); if (strcmp(ListTag, "VEGPARAMS") == 0) { uint32 nFlags = 0; nodeList->getAttr("Idle2Move", nFlags); //will go as soon as CryMannegin is up and running if (nFlags) { m_VEG_Flags |= CA_VEG_I2M; //this is a case for CryMannequin } continue; } //----------------------------------------------------------- //--- define dimensions of the LMG --- //----------------------------------------------------------- if (strcmp(ListTag, "Dimensions") == 0) { m_Dimensions = nodeList->getChildCount(); if (m_Dimensions > 4) { m_Status = "Error: More then 4 dimensions per ParaGroup are not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } for (uint32 d = 0; d < m_Dimensions; d++) { XmlNodeRef nodeExample = nodeList->getChild(d); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Param") == 0) { //each dimension must have a parameter-name m_DimPara[d].m_strParaName = nodeExample->getAttr("name"); //check if the parameter-name is supported by the system uint32 supported = 0; if (m_DimPara[d].m_strParaName == "MoveSpeed") { m_DimPara[d].m_ParaID = eMotionParamID_TravelSpeed, supported = 1; } if (m_DimPara[d].m_strParaName == "TurnSpeed") { m_DimPara[d].m_ParaID = eMotionParamID_TurnSpeed, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelSlope") { m_DimPara[d].m_ParaID = eMotionParamID_TravelSlope, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelAngle") { m_DimPara[d].m_ParaID = eMotionParamID_TravelAngle, supported = 1; } if (m_DimPara[d].m_strParaName == "TravelDist") { m_DimPara[d].m_ParaID = eMotionParamID_TravelDist, supported = 1; } if (m_DimPara[d].m_strParaName == "TurnAngle") { m_DimPara[d].m_ParaID = eMotionParamID_TurnAngle, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight, supported = 1; //these are just a custom "Blend-Node" } if (m_DimPara[d].m_strParaName == "BlendWeight2") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight2, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight3") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight3, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight4") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight4, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight5") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight5, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight6") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight6, supported = 1; } if (m_DimPara[d].m_strParaName == "BlendWeight7") { m_DimPara[d].m_ParaID = eMotionParamID_BlendWeight7, supported = 1; } if (m_DimPara[d].m_strParaName == "StopLeg") { m_DimPara[d].m_ParaID = eMotionParamID_StopLeg, supported = 1; //I don't like this. It's a case for CryManequin } if (supported == 0) { ((m_Status = "Error: The parameter '") += m_DimPara[d].m_strParaName.c_str()) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } nodeExample->getAttr("ParaScale", m_DimPara[d].m_ParaScale); nodeExample->getAttr("ChooseBlendSpace", m_DimPara[d].m_ChooseBlendSpace); //special flags per-dimension m_DimPara[d].m_nDimensionFlags = 0; uint32 flag = 0; nodeExample->getAttr("locked", flag); if (flag) { m_DimPara[d].m_nDimensionFlags |= CA_Dim_LockedParameter; } } } continue; } //----------------------------------------------------------- //--- define the additional extraction parameters --- //----------------------------------------------------------- if (strcmp(ListTag, "AdditionalExtraction") == 0) { m_ExtractionParams = nodeList->getChildCount(); if (m_ExtractionParams > 4) { m_Status = "Error: More then 4 additional extraction parameters are not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } for (uint32 d = 0; d < m_ExtractionParams; d++) { XmlNodeRef nodeExample = nodeList->getChild(d); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "Param") == 0) { //each dimension must have a parameter-name m_ExtPara[d].m_strParaName = nodeExample->getAttr("name"); //check if the parameter-name is supported by the system uint32 supported = 0; if (m_ExtPara[d].m_strParaName == "MoveSpeed") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelSpeed, supported = 1; } if (m_ExtPara[d].m_strParaName == "TurnSpeed") { m_ExtPara[d].m_ParaID = eMotionParamID_TurnSpeed, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelSlope") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelSlope, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelAngle") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelAngle, supported = 1; } if (m_ExtPara[d].m_strParaName == "TravelDist") { m_ExtPara[d].m_ParaID = eMotionParamID_TravelDist, supported = 1; } if (m_ExtPara[d].m_strParaName == "TurnAngle") { m_ExtPara[d].m_ParaID = eMotionParamID_TurnAngle, supported = 1; } if (supported == 0) { ((m_Status = "Error: The parameter '") += m_DimPara[d].m_strParaName.c_str()) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } } } continue; } if (strcmp(ListTag, "BlendSpaces") == 0) { uint32 num = nodeList->getChildCount(); m_arrCombinedBlendSpaces.resize(num); for (uint32 i = 0; i < num; i++) { XmlNodeRef nodeExample = nodeList->getChild(i); const char* ExampleTag = nodeExample->getTag(); if (strcmp(ExampleTag, "BlendSpace") == 0) { string strFilePath = nodeExample->getAttr("aname"); m_arrCombinedBlendSpaces[i].m_FilePath = strFilePath; uint32 nCRC32 = CCrc32::ComputeLowercase(strFilePath.c_str()); m_arrCombinedBlendSpaces[i].m_FilePathCRC32 = nCRC32; uint32 numLMG = g_AnimationManager.m_arrGlobalLMG.size(); for (uint32 id = 0; id < numLMG; id++) { if (g_AnimationManager.m_arrGlobalLMG[id].m_FilePathCRC32 == nCRC32) { uint32 isBSValid = g_AnimationManager.m_arrGlobalLMG[id].IsAssetLMGValid(); if (isBSValid == 0) { const char* strBSFilePath = g_AnimationManager.m_arrGlobalLMG[id].GetFilePath(); ((m_Status = "Error: The included blend-space '") += strBSFilePath) += "' is not valid"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return 0; } m_arrCombinedBlendSpaces[i].m_ParaGroupID = id; break; } } if (m_arrCombinedBlendSpaces[i].m_ParaGroupID < 0) { ((m_Status = "Error: The included file '") += strFilePath) += "' was not found"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return 0; } } else { ((m_Status = "Error: The XML-Tag '") += ExampleTag) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return 0; } } continue; } //----------------------------------------------------------- //-- joint mask //----------------------------------------------------------- if (strcmp(ListTag, "JointList") == 0) { uint32 num = nodeList->getChildCount(); m_jointList.resize(num); for (uint32 i = 0; i < num; ++i) { XmlNodeRef node = nodeList->getChild(i); const char* tag = node->getTag(); if (strcmp(tag, "Joint") != 0) { ((m_Status = "Error: The XML-Tag '") += tag) += "' is currently not supported"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return 0; } const char* name = node->getAttr("Name"); m_jointList[i] = CCrc32::Compute(name); } std::sort(m_jointList.begin(), m_jointList.end()); continue; } } } if (m_Dimensions == 0) { m_Status = "Error: ParaGroup has no dimensions specified"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } if (m_numExamples == 0 && m_arrCombinedBlendSpaces.size() == 0) { m_Status = "Error: ParaGroup has no examples specified"; gEnv->pLog->LogError("CryAnimation %s: %s", m_Status.c_str(), pathnameLMG); return false; } OnAssetLMGValid(); //everything seems to be fine!!! return true; } #if BLENDSPACE_VISUALIZATION void GlobalAnimationHeaderLMG::CreateInternalType_Para1D() { //init one dimension m_Dimensions = 1; m_DimPara[0].m_strParaName = "BlendWeight"; m_DimPara[0].m_ParaID = eMotionParamID_BlendWeight; //this is just a custom "Blend-Node" m_DimPara[0].m_min = -3.0f; m_DimPara[0].m_max = +4.0f; m_DimPara[0].m_cells = 29; //example-list m_numExamples = 2; m_arrParameter.resize(m_numExamples + 2); //2 examples + 2 pseudos //m_arrParameter[0].m_animName.SetName("stand_tac_walk_rifle_fwd_slow_3p_01"); m_arrParameter[0].m_PreInitialized[0] = 1; m_arrParameter[0].m_Para.x = 0.0f; m_arrParameter[0].i0 = 0; m_arrParameter[0].w0 = 1.0f; m_arrParameter[0].i1 = 0; m_arrParameter[0].w1 = 0.0f; //m_arrParameter[1].m_animName.SetName("stand_tac_runTurn_rifle_lft_fast_3p_01"); m_arrParameter[1].m_PreInitialized[0] = 1; m_arrParameter[1].m_Para.x = 1.0f; m_arrParameter[1].i0 = 1; m_arrParameter[1].w0 = 1.0f; m_arrParameter[1].i1 = 0; m_arrParameter[1].w1 = 0.0f; //pseudo example-list m_arrParameter[2].i0 = 0; m_arrParameter[2].i1 = 1; m_arrParameter[2].w0 = 4.2f; m_arrParameter[2].w1 = -3.2f; m_arrParameter[3].i0 = 1; m_arrParameter[3].i1 = 0; m_arrParameter[3].w0 = 4.2f; m_arrParameter[3].w1 = -3.2f; //set blend-annotation-list -- m_arrBSAnnotations.reserve(3); BSBlendable face; face.idx0 = 2; face.idx1 = 0; face.num = 2; m_arrBSAnnotations.push_back(face); face.idx0 = 0; face.idx1 = 1; face.num = 2; m_arrBSAnnotations.push_back(face); face.idx0 = 1; face.idx1 = 3; face.num = 2; m_arrBSAnnotations.push_back(face); m_ExtractionParams = 4; m_ExtPara[0].m_ParaID = eMotionParamID_TravelSpeed; m_ExtPara[1].m_ParaID = eMotionParamID_TurnSpeed; m_ExtPara[2].m_ParaID = eMotionParamID_TravelSlope; m_ExtPara[3].m_ParaID = eMotionParamID_TravelAngle; OnAssetInternalType(); OnAssetCreated(); OnAssetLMG(); OnAssetLMGValid(); } #endif #ifdef EDITOR_PCDEBUGCODE bool GlobalAnimationHeaderLMG::Export2HTR(const char* szAnimationName, const char* savePath, const CDefaultSkeleton* pDefaultSkeleton, const CSkeletonAnim* pSkeletonAnim) const { uint32 num = pSkeletonAnim->GetNumAnimsInFIFO(0); if (num == 0) { return false; } const CAnimation& Animation = pSkeletonAnim->GetAnimFromFIFO(0, 0); if (Animation.GetParametricSampler() == 0) { return false; } //------------------------------------------------------------------- std::vector<string> jointNameArray; std::vector<string> jointParentArray; const QuatT* parrDefJoints = &pDefaultSkeleton->m_poseDefaultData.GetJointsRelative()[0]; uint32 numJoints = pDefaultSkeleton->m_arrModelJoints.size(); for (uint32 j = 0; j < numJoints; j++) { const CDefaultSkeleton::SJoint* pJoint = &pDefaultSkeleton->m_arrModelJoints[j]; CRY_ASSERT(pJoint); jointNameArray.push_back(pJoint->m_strJointName.c_str()); int16 parentID = pJoint->m_idxParent; if (parentID == -1) { jointParentArray.push_back("INVALID"); } else { const CDefaultSkeleton::SJoint* parentJoint = &pDefaultSkeleton->m_arrModelJoints[parentID]; if (parentJoint) { jointParentArray.push_back(parentJoint->m_strJointName.c_str()); } } } //fetch and sum up all CAFs in this LMG const CAnimationSet* pAnimationSet = pDefaultSkeleton->m_pAnimationSet; SParametricSamplerInternal& lmg = *(SParametricSamplerInternal*)(Animation.GetParametricSampler()); f32 fTWNDeltaTime = lmg.Parameterizer(pAnimationSet, pDefaultSkeleton, Animation, 1.0f / Animation.GetSampleRate(), 1.0f, 0); uint32 nFrames = uint32(1.0f / fTWNDeltaTime + 1.5f); f32 timestep = 1.0f / f32(nFrames - 1); std::vector< DynArray<QuatT> > arrAnimation; arrAnimation.resize(numJoints); for (uint32 j = 0; j < numJoints; j++) { arrAnimation[j].resize(nFrames); } for (uint32 j = 0; j < numJoints; j++) { for (uint32 k = 0; k < nFrames; k++) { arrAnimation[j][k].q.w = 0.0f; arrAnimation[j][k].q.v.x = 0.0f; arrAnimation[j][k].q.v.y = 0.0f; arrAnimation[j][k].q.v.z = 0.0f; arrAnimation[j][k].t.x = 0.0f; arrAnimation[j][k].t.y = 0.0f; arrAnimation[j][k].t.z = 0.0f; } } for (uint32 a = 0; a < lmg.m_numExamples; a++) { int nAnimID = lmg.m_nAnimID[a]; f32 fWeight = lmg.m_fBlendWeight[a]; if (fWeight == 0.0f) { continue; } const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(nAnimID); CRY_ASSERT(pAnim->m_nAssetType == CAF_File); GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[pAnim->m_nGlobalAnimId]; const CDefaultSkeleton::SJoint* pJointRoot = &pDefaultSkeleton->m_arrModelJoints[0]; CRY_ASSERT(pJointRoot); IController* pControllerRoot = rCAF.GetControllerByJointCRC32(pJointRoot->m_nJointCRC32); f32 t = timestep; arrAnimation[0][0].q.SetIdentity(); for (uint32 k = 1; k < nFrames; k++) { QuatT qtold = parrDefJoints[0]; QuatT qtnew = parrDefJoints[0]; if (pControllerRoot) { pControllerRoot->GetOP(rCAF.NTime2KTime(t - timestep), qtold.q, qtold.t); qtold.q *= fsgnnz(parrDefJoints[0].q | qtold.q); //this could be optimized at loading-time pControllerRoot->GetOP(rCAF.NTime2KTime(t), qtnew.q, qtnew.t); qtnew.q *= fsgnnz(parrDefJoints[0].q | qtnew.q); //this could be optimized at loading-time } QuatT rel = qtold.GetInverted() * qtnew; arrAnimation[0][k].q += fWeight * rel.q; arrAnimation[0][k].t += fWeight * rel.t; t += timestep; } for (uint32 j = 1; j < numJoints; j++) { const CDefaultSkeleton::SJoint* pJoint = &pDefaultSkeleton->m_arrModelJoints[j]; CRY_ASSERT(pJoint); IController* pController = rCAF.GetControllerByJointCRC32(pJoint->m_nJointCRC32); t = 0.0f; for (uint32 k = 0; k < nFrames; k++) { QuatT qt = parrDefJoints[j]; if (pController) { pController->GetOP(rCAF.NTime2KTime(t), qt.q, qt.t); qt.q *= fsgnnz(parrDefJoints[j].q | qt.q); //this could be optimized at loading-time } arrAnimation[j][k].q += fWeight * qt.q; arrAnimation[j][k].t += fWeight * qt.t; t += timestep; } } } for (uint32 j = 0; j < numJoints; j++) { for (uint32 k = 0; k < nFrames; k++) { arrAnimation[j][k].q.Normalize(); } } for (uint32 k = 1; k < nFrames; k++) { Vec3 abs = arrAnimation[0][k - 1].t; Vec3 rel = arrAnimation[0][k].t; arrAnimation[0][k] = arrAnimation[0][k - 1] * arrAnimation[0][k]; Vec3 abs1 = arrAnimation[0][k].t; uint32 ddd = 0; } bool htr = GlobalAnimationHeaderCAF::SaveHTR(szAnimationName, savePath, jointNameArray, jointParentArray, arrAnimation, parrDefJoints); bool caf = GlobalAnimationHeaderCAF::SaveICAF(szAnimationName, savePath, jointNameArray, arrAnimation, Animation.GetSampleRate()); return true; } bool GlobalAnimationHeaderLMG::ExportVGrid(const char* savePath) const { if (!HasVGrid()) { return true; } const bool isCombBSpace = (!m_arrCombinedBlendSpaces.empty()); if (isCombBSpace) { return false; } const char* const filepath = savePath ? savePath : GetFilePath(); XmlNodeRef xmlRoot = gEnv->pSystem->LoadXmlFromFile(filepath); if (!xmlRoot) { gEnv->pLog->LogError("CryAnimation: Failed to load xml file '%s', will try to create it", filepath); return false; } XmlNodeRef xmlVGrid = xmlRoot->findChild("VGrid"); if (xmlVGrid) { xmlRoot->removeChild(xmlVGrid); } xmlVGrid = xmlRoot->createNode("VGrid"); xmlRoot->addChild(xmlVGrid); const bool writeVGridSuccess = VirtualExampleXML::WriteVGrid(xmlVGrid, *this); if (!writeVGridSuccess) { return false; } const bool saveFileSuccess = xmlRoot->saveToFile(filepath); if (!saveFileSuccess) { gEnv->pLog->LogError("CryAnimation: Failed to save xml file '%s'", filepath); return false; } return true; } bool GlobalAnimationHeaderLMG::HasVGrid() const { switch (m_Dimensions) { case 1: return m_VirtualExampleGrid1D.size() != 0; case 2: return m_VirtualExampleGrid2D.size() != 0; case 3: return m_VirtualExampleGrid3D.size() != 0; default: return false; } } #endif void GlobalAnimationHeaderLMG::ParameterExtraction(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 nParaID, uint32 d) { if (nParaID == eMotionParamID_TravelSpeed) { Init_MoveSpeed(pAnimationSet, pDefaultSkeleton, d); } if (nParaID == eMotionParamID_TurnSpeed) { Init_TurnSpeed(pAnimationSet, pDefaultSkeleton, d); } if (nParaID == eMotionParamID_TurnAngle) { Init_TurnAngle(pAnimationSet, pDefaultSkeleton, d); } if (nParaID == eMotionParamID_TravelAngle) { Init_TravelAngle(pAnimationSet, pDefaultSkeleton, d); } if (nParaID == eMotionParamID_TravelSlope) { Init_SlopeAngle(pAnimationSet, pDefaultSkeleton, d); } if (nParaID == eMotionParamID_TravelDist) { Init_TravelDist(pAnimationSet, pDefaultSkeleton, d); } } bool GlobalAnimationHeaderLMG::LoadAndCheckValidParametricAnim(const CAnimationSet* pAnimationSet, uint32 id, GlobalAnimationHeaderCAF*& caf) { int32 animID = pAnimationSet->GetAnimIDByCRC(m_arrParameter[id].m_animName.m_CRC32); CRY_ASSERT(animID >= 0); int32 globalID = pAnimationSet->GetGlobalIDByAnimID_Fast(animID); CRY_ASSERT(globalID >= 0 && globalID < g_AnimationManager.m_arrGlobalCAF.size()); if (globalID >= 0 && globalID < g_AnimationManager.m_arrGlobalCAF.size()) { caf = &g_AnimationManager.m_arrGlobalCAF[globalID]; if (caf->IsAssetLoaded()) { return true; } else { CryFatalError("CryAnimation: asset for parameter-extraction not in memory: %s", caf->GetFilePath()); return false; } } return false; } void GlobalAnimationHeaderLMG::Init_MoveSpeed(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples;//m_arrBSAnimations2.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } DynArray<Vec3> root_keys; root_keys.resize(numKeys); f32 fk = rCAF.m_fStartSec * rCAF.GetSampleRate(); for (uint32 k = 0; k < numKeys; k++, fk += 1.0f) { pController->GetP(fk, root_keys[k]); } uint32 skey = uint32(m_DimPara[dim].m_skey * (numKeys - 1)); uint32 ekey = uint32(m_DimPara[dim].m_ekey * (numKeys - 1)); uint32 poses = 0; f32 movespeed = 0.0f; for (uint32 k = skey; k < ekey; k++) { movespeed += (root_keys[k + 0] - root_keys[k + 1]).GetLength() * rCAF.GetSampleRate(); poses++; } if (poses) { movespeed /= poses; } m_arrParameter[i].m_Para[dim] = movespeed * m_arrParameter[i].m_fPlaybackScale; } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::Init_TurnSpeed(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples;//m_arrBSAnimations2.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } DynArray<Vec3> pos_keys; DynArray<Quat> rot_keys; pos_keys.resize(numKeys); rot_keys.resize(numKeys); f32 fk = rCAF.m_fStartSec * rCAF.GetSampleRate(); for (uint32 k = 0; k < numKeys; k++, fk += 1.0f) { pController->GetOP(fk, rot_keys[k], pos_keys[k]); } uint32 skey = uint32(m_DimPara[dim].m_skey * (numKeys - 1)); uint32 ekey = uint32(m_DimPara[dim].m_ekey * (numKeys - 1)); uint32 poses = 0; f32 turnspeed = 0.0f; for (uint32 k = 0; k < ekey; k++) { Vec3 v0 = rot_keys[k + 0].GetColumn1(); Vec3 v1 = rot_keys[k + 1].GetColumn1(); turnspeed += Ang3::CreateRadZ(v0, v1) * rCAF.GetSampleRate(); poses++; } if (poses) { turnspeed /= poses; } m_arrParameter[i].m_Para[dim] = turnspeed; } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::Init_TurnAngle(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples; //m_arrBSAnimations2.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } DynArray<Quat> rot_keys; rot_keys.resize(numKeys); f32 fk = rCAF.m_fStartSec * rCAF.GetSampleRate(); for (uint32 k = 0; k < numKeys; k++, fk += 1.0f) { pController->GetO(fk, rot_keys[k]); } uint32 skey = uint32(m_DimPara[dim].m_skey * (numKeys - 1)); uint32 ekey = uint32(m_DimPara[dim].m_ekey * (numKeys - 1)); f32 turnangle = 0.0f; for (uint32 k = 0; k < ekey; k++) { Vec3 v0 = rot_keys[k + 0].GetColumn1(); Vec3 v1 = rot_keys[k + 1].GetColumn1(); turnangle += Ang3::CreateRadZ(v0, v1); } m_arrParameter[i].m_Para[dim] = turnangle; } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::Init_TravelAngle(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples;//m_arrBSAnimations2.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } DynArray<Vec3> pos_keys; DynArray<Quat> rot_keys; pos_keys.resize(numKeys); rot_keys.resize(numKeys); f32 fk = rCAF.m_fStartSec * rCAF.GetSampleRate(); for (uint32 k = 0; k < numKeys; k++, fk += 1.0f) { pController->GetOP(fk, rot_keys[k], pos_keys[k]); } uint32 skey = uint32(m_DimPara[dim].m_skey * (numKeys - 1)); uint32 ekey = uint32(m_DimPara[dim].m_ekey * (numKeys - 1)); f32 fTravelAngle = 0.0f; Vec3 totalMovement(0.0f, 0.0f, 0.0f); for (uint32 k = skey; k < ekey; k++) { const Vec3 movement = pos_keys[k + 1] - pos_keys[k + 0]; totalMovement += !rot_keys[k + 1] * movement; } Vec3 v0(0, 1, 0); Vec3 v1 = (totalMovement).GetNormalizedSafe(Vec3(0, 1, 0)); m_arrParameter[i].m_Para[dim] = Ang3::CreateRadZ(v0, v1); } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::Init_SlopeAngle(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples;//m_arrBSAnimations2.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } DynArray<Vec3> pos_keys; DynArray<Quat> rot_keys; pos_keys.resize(numKeys); rot_keys.resize(numKeys); f32 fk = rCAF.m_fStartSec * rCAF.GetSampleRate(); ; for (uint32 k = 0; k < numKeys; k++, fk += 1.0f) { pController->GetOP(fk, rot_keys[k], pos_keys[k]); } uint32 skey = uint32(m_DimPara[dim].m_skey * (numKeys - 1)); uint32 ekey = uint32(m_DimPara[dim].m_ekey * (numKeys - 1)); uint32 poses = 0; f32 fSlopeAngle = 0.0f; for (uint32 k = 0; k < ekey; k++) { Vec3 rel = (pos_keys[k + 1] - pos_keys[k + 0]).GetNormalizedSafe(Vec3(0, 1, 0)); f32 tdir = atan2_tpl(-rel.x, rel.y); Vec3 v = rel * Matrix33::CreateRotationZ(tdir); fSlopeAngle += atan2_tpl(v.z, v.y); poses++; } if (poses) { fSlopeAngle /= poses; } m_arrParameter[i].m_Para[dim] = fSlopeAngle; } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::Init_TravelDist(const CAnimationSet* pAnimationSet, const CDefaultSkeleton* pDefaultSkeleton, uint32 dim) { uint32 init_count = 0; uint32 numAssets = m_numExamples; //m_arrBSAnimations.size(); for (uint32 i = 0; i < numAssets; i++) { GlobalAnimationHeaderCAF* pCAF = nullptr; if (!LoadAndCheckValidParametricAnim(pAnimationSet, i, pCAF)) { break; } GlobalAnimationHeaderCAF& rCAF = *pCAF; const CDefaultSkeleton::SJoint* pRootJoint = &pDefaultSkeleton->m_arrModelJoints[0]; IController* pController = rCAF.GetControllerByJointCRC32(pRootJoint->m_nJointCRC32); if (pController == 0) { continue; } f32 duration = rCAF.m_fEndSec - rCAF.m_fStartSec; uint32 numKeys = uint32(duration * rCAF.GetSampleRate() + 1); if (numKeys == 1) { continue; } init_count++; if (m_arrParameter[i].m_PreInitialized[dim]) { continue; //already pre-initialized } QuatT key0; pController->GetOP(0.0f, key0.q, key0.t); QuatT key1; pController->GetOP(rCAF.NTime2KTime(1), key1.q, key1.t); f32 fTravelDist = (key1.t - key0.t).GetLength(); f32 anf3 = m_arrParameter[i].m_Para[dim]; m_arrParameter[i].m_Para[dim] = fTravelDist; } if (init_count == numAssets) { m_DimPara[dim].m_nInitialized = true; } } void GlobalAnimationHeaderLMG::EnsureValidFaceExampleIndex(uint8& idx, uint32 num) { uint32 maxIdx = num - 1; if (idx < 0 || idx > maxIdx) { CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_ERROR, "Animation: BlendSpace face example index %u is out of range [0, %u] and has been clamped. Fix it in order to work properly.", idx, maxIdx); idx = clamp_tpl<uint8>(idx, 0, maxIdx); } }