void FVertexAnimTools::ImportVertexAnimtion(UnFbx::FFbxImporter* FFbxImporter, USkeletalMesh * SkelMesh, UPackage * Package, FString& Filename) { check(SkelMesh); check(SkelMesh->GetImportedResource()->LODModels.Num() > 0); FStaticLODModel& Model = SkelMesh->GetImportedResource()->LODModels[0]; if(Model.MeshToImportVertexMap.Num() == 0) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: Mesh does not contain necessary vertex anim mapping.")); return; } FbxNode* RootNodeToImport = FFbxImporter->Scene->GetRootNode(); /* Exporting from Maya with skin and vertex cache seems to throw away the skin deformer, so not sure how to find the 'right' mesh... TArray<FbxNode*> MeshArray; FName RootBoneName = (SkelMesh->Skeleton)? SkelMesh->Skeleton->GetBoneName(0) : SkelMesh->RefSkeleton.RefBoneInfo(0).Name; FFbxImporter->FindFBXMeshesByBone(RootBoneName, false, MeshArray); */ // Find mesh with vertex deformer TArray<FbxMesh*> VertexCacheMeshes; RecursiveFindVertexAnimMeshes(RootNodeToImport, VertexCacheMeshes); if(VertexCacheMeshes.Num() == 0) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: No vertex cache deformed mesh found.")); return; } // Get deformer and open cache FbxMesh* Mesh = VertexCacheMeshes[0]; FbxVertexCacheDeformer* Deformer = static_cast<FbxVertexCacheDeformer*>(Mesh->GetDeformer(0, FbxDeformer::eVertexCache)); check(Deformer != NULL); // tested in RecursiveFindVertexAnimMeshes FbxCache* Cache = Deformer->GetCache(); bool bOpenSuccess = Cache->OpenFileForRead(); if(!bOpenSuccess) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: Unable to open cache file.")); return; } // Check cache format if(Cache->GetCacheFileFormat() != FbxCache::eMayaCache) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: Only Maya vertex animation supported.")); return; } // Get channel FbxString DeformerChannelName = Deformer->Channel.Get(); int ChannelIndex = Cache->GetChannelIndex(DeformerChannelName); if(ChannelIndex == -1) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: No Cache channel for deformer.")); return; } // Get data type FbxCache::EMCDataType DataType; bool bGetDataTypeOk = Cache->GetChannelDataType(ChannelIndex, DataType); check(bGetDataTypeOk); if(DataType != FbxCache::EMCDataType::eDoubleVectorArray) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: Only double array format supported at the moment.")); return; } // Get other info about anim FbxTime StartTime, DeltaTime; int32 NumFrames, NumAnimatedPoints; bool bInfoOk = GetVertexAnimInfo(Cache, ChannelIndex, StartTime, DeltaTime, NumFrames, NumAnimatedPoints); if(!bInfoOk) { UE_LOG(LogFbx, Warning, TEXT("ImportVertexAnimtion: Invalid vertex animation.")); return; } UVertexAnimation* NewVertexAnim = UnFbx::FFbxImporter::CreateAsset<UVertexAnimation>( Package->GetName(), *FPaths::GetBaseFilename(Filename) ); if (NewVertexAnim != NULL) { // Save the mesh we are importing for NewVertexAnim->BaseSkelMesh = SkelMesh; NewVertexAnim->NumAnimatedVerts = NumAnimatedPoints; ConvertCacheToAnim(Cache, ChannelIndex, NewVertexAnim, StartTime, DeltaTime, NumFrames); UE_LOG(LogFbx, Log, TEXT("ImportVertexAnimtion: Success! %d frames."), NewVertexAnim->GetNumFrames()); } }
void PreparePointCacheData(FbxScene* pScene, FbxTime &pCache_Start, FbxTime &pCache_Stop) { // This function show how to cycle through scene elements in a linear way. const int lNodeCount = pScene->GetSrcObjectCount<FbxNode>(); for (int lIndex=0; lIndex<lNodeCount; lIndex++) { FbxNode* lNode = pScene->GetSrcObject<FbxNode>(lIndex); if (lNode->GetGeometry()) { int i, lVertexCacheDeformerCount = lNode->GetGeometry()->GetDeformerCount(FbxDeformer::eVertexCache); // There should be a maximum of 1 Vertex Cache Deformer for the moment lVertexCacheDeformerCount = lVertexCacheDeformerCount > 0 ? 1 : 0; for (i=0; i<lVertexCacheDeformerCount; ++i ) { // Get the Point Cache object FbxVertexCacheDeformer* lDeformer = static_cast<FbxVertexCacheDeformer*>(lNode->GetGeometry()->GetDeformer(i, FbxDeformer::eVertexCache)); if( !lDeformer ) continue; FbxCache* lCache = lDeformer->GetCache(); if( !lCache ) continue; // Process the point cache data only if the constraint is active if (lDeformer->IsActive()) { if (lCache->GetCacheFileFormat() == FbxCache::eMaxPointCacheV2) { // This code show how to convert from PC2 to MC point cache format // turn it on if you need it. #if 0 if (!lCache->ConvertFromPC2ToMC(FbxCache::eMCOneFile, FbxTime::GetFrameRate(pScene->GetGlobalTimeSettings().GetTimeMode()))) { // Conversion failed, retrieve the error here FbxString lTheErrorIs = lCache->GetError().GetLastErrorString(); } #endif } else if (lCache->GetCacheFileFormat() == FbxCache::eMayaCache) { // This code show how to convert from MC to PC2 point cache format // turn it on if you need it. //#if 0 if (!lCache->ConvertFromMCToPC2(FbxTime::GetFrameRate(pScene->GetGlobalSettings().GetTimeMode()), 0)) { // Conversion failed, retrieve the error here FbxString lTheErrorIs = lCache->GetError().GetLastErrorString(); } //#endif } // Now open the cache file to read from it if (!lCache->OpenFileForRead()) { // Cannot open file FbxString lTheErrorIs = lCache->GetError().GetLastErrorString(); // Set the deformer inactive so we don't play it back lDeformer->SetActive(false); } else { // get the start and stop time of the cache int lChannelCount = lCache->GetChannelCount(); for (int iChannelNo=0; iChannelNo < lChannelCount; iChannelNo++) { FbxTime lChannel_Start; FbxTime lChannel_Stop; if(lCache->GetAnimationRange(iChannelNo, lChannel_Start, lChannel_Stop)) { // get the smallest start time if(lChannel_Start < pCache_Start) pCache_Start = lChannel_Start; // get the biggest stop time if(lChannel_Stop > pCache_Stop) pCache_Stop = lChannel_Stop; } } } } } } } }