EReimportResult::Type UReimportFbxSceneFactory::ImportStaticMesh(void* VoidFbxImporter, TSharedPtr<FFbxMeshInfo> MeshInfo, TSharedPtr<FFbxSceneInfo> SceneInfoPtr) { UnFbx::FFbxImporter* FbxImporter = (UnFbx::FFbxImporter*)VoidFbxImporter; //FEditorDelegates::OnAssetPreImport.Broadcast(this, UStaticMesh::StaticClass(), GWorld, FName(""), TEXT("fbx")); FbxNode *GeometryParentNode = nullptr; //Get the first parent geometry node for (int idx = 0; idx < FbxImporter->Scene->GetGeometryCount(); ++idx) { FbxGeometry *Geometry = FbxImporter->Scene->GetGeometry(idx); if (Geometry->GetUniqueID() == MeshInfo->UniqueId) { GeometryParentNode = Geometry->GetNode(); break; } } if (GeometryParentNode == nullptr) { FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString("Reimport Mesh {0} fail, the mesh dont have any parent node inside the fbx."), FText::FromString(MeshInfo->GetImportPath()))), FName(TEXT("Reimport Fbx Scene"))); return EReimportResult::Failed; } FString PackageName = MeshInfo->GetImportPath(); FString StaticMeshName; UPackage* Pkg = CreatePackageForNode(PackageName, StaticMeshName); if (Pkg == nullptr) { return EReimportResult::Failed; } //Copy default options to StaticMeshImportData SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh); SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); UnFbx::FBXImportOptions* OverrideImportSettings = GetOptionsFromName(MeshInfo->OptionName); if (OverrideImportSettings != nullptr) { SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(OverrideImportSettings, GlobalImportSettings); SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(OverrideImportSettings, SceneImportOptionsStaticMesh); } else { SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettingsReference, GlobalImportSettings); SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh); } SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions); FName StaticMeshFName = FName(*(MeshInfo->Name)); UStaticMesh *NewObject = FbxImporter->ImportStaticMesh(Pkg, GeometryParentNode, StaticMeshFName, EObjectFlags::RF_Standalone, StaticMeshImportData ); if (NewObject == nullptr) { return EReimportResult::Failed; } AllNewAssets.Add(MeshInfo, NewObject); AssetToSyncContentBrowser.Add(NewObject); return EReimportResult::Succeeded; }
void MaterialHandler::MapMaterials(FbxNode * pNode, SceneMap * sceneMap) { //Recursively extract the children for (int j = 0; j < pNode->GetChildCount(); j++) MapMaterials(pNode->GetChild(j), sceneMap); FbxGeometry* pGeometry = pNode->GetGeometry(); int materialCount = 0; FbxNode* node = NULL; if (pGeometry) { node = pGeometry->GetNode(); if (node) materialCount = pNode->GetMaterialCount(); if (materialCount > 0) { for (int i = 0; i < materialCount; i++) { if (pNode->GetMaterial(i)) { FbxSurfaceMaterial *pMaterial = node->GetMaterial(i); std::cout << pMaterial->GetName() << "\n"; if (sceneMap->materialHash.find(pMaterial->GetName()) == sceneMap->materialHash.end()) { // not found std::cout << "New material found! mapping..\n"; sceneMap->materialHash[pMaterial->GetName()] = sceneMap->materialID; sceneMap->materialID += 1; } else { // found std::cout << "Material " << pMaterial->GetName() << " already exist .. skipping mapping of material" << std::endl; } //ProcessData(pMaterial, materialCount, outputMat); } } } } }
void MaterialHandler::GetMaterialData(FbxNode * pNode, MaterialExport* outputMat, SceneMap* sceneMap) { //Recursively extract the children for (int j = 0; j < pNode->GetChildCount(); j++) GetMaterialData(pNode->GetChild(j),outputMat,sceneMap); if (outputMat == nullptr) //If the first material is processed. create a new MaterialExport object { outputMat = new MaterialExport; } FbxGeometry* pGeometry = pNode->GetGeometry(); int materialCount = 0; FbxNode* node = NULL; if (pGeometry) { //If the node has any geometry node = pGeometry->GetNode(); if (node) materialCount = pNode->GetMaterialCount(); //Get the amount of materials the geometry has if (materialCount > 0) { for (int i = 0; i < materialCount; i++) { if (pNode->GetMaterial(i)) { FbxSurfaceMaterial *pMaterial = node->GetMaterial(i); //Get the material ProcessData(pMaterial, materialCount, outputMat,sceneMap); //process the data! } } } } }
void Tools::DisplayAnimation::DisplayChannels( FbxNode* i_node, FbxAnimLayer* i_animLayer, void (*DisplayCurve) (FbxAnimCurve* i_curve), void (*DisplayListCurve) (FbxAnimCurve* i_curve, FbxProperty* i_property), bool isSwitcher ) { FbxAnimCurve *animCurve = NULL; // Display general curves. if (!isSwitcher) { animCurve = i_node->LclTranslation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_X ); if( animCurve ) { FBXSDK_printf( " TX\n" ); DisplayCommon::DisplayString( " TX" ); DisplayCurve( animCurve ); } animCurve = i_node->LclTranslation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Y ); if( animCurve ) { FBXSDK_printf( " TY\n" ); DisplayCommon::DisplayString( " TY" ); DisplayCurve( animCurve ); } animCurve = i_node->LclTranslation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Z ); if( animCurve ) { FBXSDK_printf( " TZ\n" ); DisplayCommon::DisplayString( " TZ" ); DisplayCurve( animCurve ); } animCurve = i_node->LclRotation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_X ); if( animCurve ) { FBXSDK_printf( " RX\n" ); DisplayCommon::DisplayString( " RX" ); DisplayCurve( animCurve ); } animCurve = i_node->LclRotation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Y ); if( animCurve ) { FBXSDK_printf( " RY\n" ); DisplayCommon::DisplayString( " RY" ); DisplayCurve( animCurve ); } animCurve = i_node->LclRotation.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Z ); if( animCurve ) { FBXSDK_printf( " RZ\n" ); DisplayCommon::DisplayString( " RZ" ); DisplayCurve( animCurve ); } animCurve = i_node->LclScaling.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_X ); if( animCurve ) { FBXSDK_printf( " SX\n" ); DisplayCommon::DisplayString( " SX" ); DisplayCurve( animCurve ); } animCurve = i_node->LclScaling.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Y ); if( animCurve ) { FBXSDK_printf( " SY\n" ); DisplayCommon::DisplayString( " SY" ); DisplayCurve( animCurve ); } animCurve = i_node->LclScaling.GetCurve( i_animLayer, FBXSDK_CURVENODE_COMPONENT_Z ); if( animCurve ) { FBXSDK_printf( " SZ\n" ); DisplayCommon::DisplayString( " SZ" ); DisplayCurve( animCurve ); } } // Display curves specific to a light or marker. FbxNodeAttribute *nodeAttribute = i_node->GetNodeAttribute(); if( nodeAttribute ) { animCurve = nodeAttribute->Color.GetCurve( i_animLayer, FBXSDK_CURVENODE_COLOR_RED ); if( animCurve ) { FBXSDK_printf( " Red\n" ); DisplayCommon::DisplayString( " Red" ); DisplayCurve( animCurve ); } animCurve = nodeAttribute->Color.GetCurve( i_animLayer, FBXSDK_CURVENODE_COLOR_GREEN ); if( animCurve ) { FBXSDK_printf( " Green\n" ); DisplayCommon::DisplayString( " Green" ); DisplayCurve( animCurve ); } animCurve = nodeAttribute->Color.GetCurve( i_animLayer, FBXSDK_CURVENODE_COLOR_BLUE ); if( animCurve ) { FBXSDK_printf( " Blue\n" ); DisplayCommon::DisplayString( " Blue" ); DisplayCurve( animCurve ); } // Display curves specific to a light. FbxLight *light = i_node->GetLight(); if( light ) { animCurve = light->Intensity.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Intensity\n" ); DisplayCommon::DisplayString( " Intensity" ); DisplayCurve( animCurve ); } animCurve = light->OuterAngle.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Outer Angle\n" ); DisplayCommon::DisplayString( " Outer Angle" ); DisplayCurve( animCurve ); } animCurve = light->Fog.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Fog\n" ); DisplayCommon::DisplayString( " Fog" ); DisplayCurve( animCurve ); } } // Display curves specific to a camera. FbxCamera *camera = i_node->GetCamera(); if( camera ) { animCurve = camera->FieldOfView.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Field of View\n" ); DisplayCommon::DisplayString( " Field of View" ); DisplayCurve( animCurve ); } animCurve = camera->FieldOfViewX.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Field of View X\n" ); DisplayCommon::DisplayString( " Field of View X" ); DisplayCurve( animCurve ); } animCurve = camera->FieldOfViewY.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Field of View Y\n" ); DisplayCommon::DisplayString( " Field of View Y" ); DisplayCurve( animCurve ); } animCurve = camera->OpticalCenterX.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Optical Center X\n" ); DisplayCommon::DisplayString( " Optical Center X" ); DisplayCurve( animCurve ); } animCurve = camera->OpticalCenterY.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Optical Center Y\n" ); DisplayCommon::DisplayString( " Optical Center Y" ); DisplayCurve( animCurve ); } animCurve = camera->Roll.GetCurve( i_animLayer ); if( animCurve ) { FBXSDK_printf( " Roll\n" ); DisplayCommon::DisplayString( " Roll" ); DisplayCurve( animCurve ); } } // Display curves specific to a geometry. if (nodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh || nodeAttribute->GetAttributeType() == FbxNodeAttribute::eNurbs || nodeAttribute->GetAttributeType() == FbxNodeAttribute::ePatch) { FbxGeometry *geometry = (FbxGeometry*) nodeAttribute; int blendShapeDeformerCount = geometry->GetDeformerCount( FbxDeformer::eBlendShape ); for( int blendShapeIndex = 0; blendShapeIndex<blendShapeDeformerCount; blendShapeIndex++ ) { FbxBlendShape *blendShape = (FbxBlendShape*)geometry->GetDeformer( blendShapeIndex, FbxDeformer::eBlendShape ); int blendShapeChannelCount = blendShape->GetBlendShapeChannelCount(); for( int channelIndex = 0; channelIndex<blendShapeChannelCount; channelIndex++ ) { FbxBlendShapeChannel *channel = blendShape->GetBlendShapeChannel( channelIndex ); const char *channelName = channel->GetName(); animCurve = geometry->GetShapeChannel( blendShapeIndex, channelIndex, i_animLayer, true ); if( animCurve ) { FBXSDK_printf( " Shape %s\n", channelName ); DisplayCommon::DisplayString( " Shape ", channelName ); DisplayCurve( animCurve ); } } } } } // Display curves specific to properties FbxProperty lProperty = i_node->GetFirstProperty(); while( lProperty.IsValid() ) { if( lProperty.GetFlag(FbxPropertyAttr::eUserDefined) ) { FbxString lFbxFCurveNodeName = lProperty.GetName(); FbxAnimCurveNode* curveNode = lProperty.GetCurveNode( i_animLayer ); if( !curveNode ) { lProperty = i_node->GetNextProperty( lProperty ); continue; } FbxDataType dataType = lProperty.GetPropertyDataType(); if( dataType.GetType() == eFbxBool || dataType.GetType() == eFbxDouble || dataType.GetType() == eFbxFloat || dataType.GetType() == eFbxInt ) { FbxString message; message = " Property "; message += lProperty.GetName(); if( lProperty.GetLabel().GetLen() > 0 ) { message += " (Label: "; message += lProperty.GetLabel(); message += ")"; }; DisplayCommon::DisplayString( message ); for( int c = 0; c < curveNode->GetCurveCount(0U); c++ ) { animCurve = curveNode->GetCurve( 0U, c ); if( animCurve ) DisplayCurve( animCurve ); } } else if( dataType.GetType() == eFbxDouble3 || dataType.GetType() == eFbxDouble4 || dataType.Is(FbxColor3DT) || dataType.Is(FbxColor4DT) ) { char* componentName1 = (dataType.Is(FbxColor3DT) ||dataType.Is(FbxColor4DT)) ? (char*)FBXSDK_CURVENODE_COLOR_RED : (char*)"X"; char* componentName2 = (dataType.Is(FbxColor3DT) ||dataType.Is(FbxColor4DT)) ? (char*)FBXSDK_CURVENODE_COLOR_GREEN : (char*)"Y"; char* componentName3 = (dataType.Is(FbxColor3DT) ||dataType.Is(FbxColor4DT)) ? (char*)FBXSDK_CURVENODE_COLOR_BLUE : (char*)"Z"; FbxString message; message = " Property "; message += lProperty.GetName(); if( lProperty.GetLabel().GetLen() > 0 ) { message += " (Label: "; message += lProperty.GetLabel(); message += ")"; } DisplayCommon::DisplayString( message ); for( int c = 0; c < curveNode->GetCurveCount(0U); c++ ) { animCurve = curveNode->GetCurve( 0U, c ); if( animCurve ) { DisplayCommon::DisplayString( " Component ", componentName1 ); DisplayCurve( animCurve ); } } for( int c = 0; c < curveNode->GetCurveCount(1U); c++ ) { animCurve = curveNode->GetCurve(1U, c); if( animCurve ) { DisplayCommon::DisplayString( " Component ", componentName2 ); DisplayCurve( animCurve ); } } for( int c = 0; c < curveNode->GetCurveCount(2U); c++ ) { animCurve = curveNode->GetCurve( 2U, c ); if( animCurve ) { DisplayCommon::DisplayString( " Component ", componentName3 ); DisplayCurve( animCurve ); } } } else if( dataType.GetType() == eFbxEnum ) { FbxString message; message = " Property "; message += lProperty.GetName(); if( lProperty.GetLabel().GetLen() > 0 ) { message += " (Label: "; message += lProperty.GetLabel(); message += ")"; }; DisplayCommon::DisplayString( message ); for( int c = 0; c < curveNode->GetCurveCount(0U); c++ ) { animCurve = curveNode->GetCurve( 0U, c ); if( animCurve ) DisplayListCurve( animCurve, &lProperty ); } } } lProperty = i_node->GetNextProperty( lProperty ); } // while }
/** * Adds Fbx Clusters necessary to skin a skeletal mesh to the bones in the BoneNodes list */ void FFbxExporter::BindMeshToSkeleton(const USkeletalMesh* SkelMesh, FbxNode* MeshRootNode, TArray<FbxNode*>& BoneNodes) { const FSkeletalMeshResource* SkelMeshResource = SkelMesh->GetImportedResource(); const FStaticLODModel& SourceModel = SkelMeshResource->LODModels[0]; const int32 VertexCount = SourceModel.NumVertices; FbxAMatrix MeshMatrix; FbxScene* lScene = MeshRootNode->GetScene(); if( lScene ) { MeshMatrix = MeshRootNode->EvaluateGlobalTransform(); } FbxGeometry* MeshAttribute = (FbxGeometry*) MeshRootNode->GetNodeAttribute(); FbxSkin* Skin = FbxSkin::Create(Scene, ""); const int32 BoneCount = BoneNodes.Num(); for(int32 BoneIndex = 0; BoneIndex < BoneCount; ++BoneIndex) { FbxNode* BoneNode = BoneNodes[BoneIndex]; // Create the deforming cluster FbxCluster *CurrentCluster = FbxCluster::Create(Scene,""); CurrentCluster->SetLink(BoneNode); CurrentCluster->SetLinkMode(FbxCluster::eTotalOne); // Add all the vertices that are weighted to the current skeletal bone to the cluster // NOTE: the bone influence indices contained in the vertex data are based on a per-chunk // list of verts. The convert the chunk bone index to the mesh bone index, the chunk's // boneMap is needed int32 VertIndex = 0; const int32 SectionCount = SourceModel.Sections.Num(); for(int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex) { const FSkelMeshSection& Section = SourceModel.Sections[SectionIndex]; for(int32 SoftIndex = 0; SoftIndex < Section.SoftVertices.Num(); ++SoftIndex) { const FSoftSkinVertex& Vert = Section.SoftVertices[SoftIndex]; for(int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex) { int32 InfluenceBone = Section.BoneMap[ Vert.InfluenceBones[InfluenceIndex] ]; float InfluenceWeight = Vert.InfluenceWeights[InfluenceIndex] / 255.f; if(InfluenceBone == BoneIndex && InfluenceWeight > 0.f) { CurrentCluster->AddControlPointIndex(VertIndex, InfluenceWeight); } } ++VertIndex; } } // Now we have the Patch and the skeleton correctly positioned, // set the Transform and TransformLink matrix accordingly. CurrentCluster->SetTransformMatrix(MeshMatrix); FbxAMatrix LinkMatrix; if( lScene ) { LinkMatrix = BoneNode->EvaluateGlobalTransform(); } CurrentCluster->SetTransformLinkMatrix(LinkMatrix); // Add the clusters to the mesh by creating a skin and adding those clusters to that skin. Skin->AddCluster(CurrentCluster); } // Add the skin to the mesh after the clusters have been added MeshAttribute->AddDeformer(Skin); }
bool UnFbx::FFbxImporter::ImportAnimation(USkeleton* Skeleton, UAnimSequence * DestSeq, const FString& FileName, TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, const int32 ResampleRate, const FbxTimeSpan AnimTimeSpan) { // @todo : the length might need to change w.r.t. sampling keys FbxTime SequenceLength = AnimTimeSpan.GetDuration(); float PreviousSequenceLength = DestSeq->SequenceLength; // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length DestSeq->SequenceLength = FGenericPlatformMath::Max<float>(SequenceLength.GetSecondDouble(), MINIMUM_ANIMATION_LENGTH); if(PreviousSequenceLength > MINIMUM_ANIMATION_LENGTH && DestSeq->RawCurveData.FloatCurves.Num() > 0) { // The sequence already existed when we began the import. We need to scale the key times for all curves to match the new // duration before importing over them. This is to catch any user-added curves float ScaleFactor = DestSeq->SequenceLength / PreviousSequenceLength; for(FFloatCurve& Curve : DestSeq->RawCurveData.FloatCurves) { Curve.FloatCurve.ScaleCurve(0.0f, ScaleFactor); } } if (ImportOptions->bDeleteExistingMorphTargetCurves) { for (int32 CurveIdx=0; CurveIdx<DestSeq->RawCurveData.FloatCurves.Num(); ++CurveIdx) { auto& Curve = DestSeq->RawCurveData.FloatCurves[CurveIdx]; if (Curve.GetCurveTypeFlag(ACF_DrivesMorphTarget)) { DestSeq->RawCurveData.FloatCurves.RemoveAt(CurveIdx, 1, false); --CurveIdx; } } DestSeq->RawCurveData.FloatCurves.Shrink(); } // // import blend shape curves // { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Morph Target Curves"), true); for ( int32 NodeIndex = 0; NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); const int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); FString BlendShapeName = UTF8_TO_TCHAR(MakeName(BlendShape->GetName())); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { FString ChannelName = UTF8_TO_TCHAR(MakeName(Channel->GetName())); // Maya adds the name of the blendshape and an underscore to the front of the channel name, so remove it if(ChannelName.StartsWith(BlendShapeName)) { ChannelName = ChannelName.Right(ChannelName.Len() - (BlendShapeName.Len()+1)); } FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { FFormatNamedArguments Args; Args.Add(TEXT("BlendShape"), FText::FromString(ChannelName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingMorphTargetCurvesDetail", "Importing Morph Target Curves [{BlendShape}]"), Args); GWarn->StatusUpdate(NodeIndex + 1, NodeArray.Num(), StatusUpate); // now see if we have one already exists. If so, just overwrite that. if not, add new one. ImportCurveToAnimSequence(DestSeq, *ChannelName, Curve, ACF_DrivesMorphTarget | ACF_TriggerEvent, AnimTimeSpan, 0.01f /** for some reason blend shape values are coming as 100 scaled **/); } } } } } } GWarn->EndSlowTask(); } // // importing custom attribute START // if (ImportOptions->bImportCustomAttribute) { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Custom Attirubte Curves"), true); const int32 TotalLinks = SortedLinks.Num(); int32 CurLinkIndex=0; for(auto Node: SortedLinks) { FbxProperty Property = Node->GetFirstProperty(); while (Property.IsValid()) { FbxAnimCurveNode* CurveNode = Property.GetCurveNode(); // do this if user defined and animated and leaf node if( CurveNode && Property.GetFlag(FbxPropertyAttr::eUserDefined) && CurveNode->IsAnimated() && IsSupportedCurveDataType(Property.GetPropertyDataType().GetType()) ) { FString CurveName = UTF8_TO_TCHAR(CurveNode->GetName()); UE_LOG(LogFbx, Log, TEXT("CurveName : %s"), *CurveName ); int32 TotalCount = CurveNode->GetChannelsCount(); for (int32 ChannelIndex=0; ChannelIndex<TotalCount; ++ChannelIndex) { FbxAnimCurve * AnimCurve = CurveNode->GetCurve(ChannelIndex); FString ChannelName = CurveNode->GetChannelName(ChannelIndex).Buffer(); if (AnimCurve) { FString FinalCurveName; if (TotalCount == 1) { FinalCurveName = CurveName; } else { FinalCurveName = CurveName + "_" + ChannelName; } FFormatNamedArguments Args; Args.Add(TEXT("CurveName"), FText::FromString(FinalCurveName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingCustomAttributeCurvesDetail", "Importing Custom Attribute [{CurveName}]"), Args); GWarn->StatusUpdate(CurLinkIndex + 1, TotalLinks, StatusUpate); ImportCurveToAnimSequence(DestSeq, FinalCurveName, AnimCurve, ACF_DefaultCurve, AnimTimeSpan); } } } Property = Node->GetNextProperty(Property); } CurLinkIndex++; } GWarn->EndSlowTask(); } // importing custom attribute END const bool bSourceDataExists = (DestSeq->SourceRawAnimationData.Num() > 0); TArray<AnimationTransformDebug::FAnimationTransformDebugData> TransformDebugData; int32 TotalNumKeys = 0; const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); // import animation { GWarn->BeginSlowTask( LOCTEXT("BeginImportAnimation", "Importing Animation"), true); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists? DestSeq->SourceRawAnimationData : DestSeq->RawAnimationData; DestSeq->TrackToSkeletonMapTable.Empty(); DestSeq->AnimationTrackNames.Empty(); RawAnimationData.Empty(); TArray<FName> FbxRawBoneNames; FillAndVerifyBoneNames(Skeleton, SortedLinks, FbxRawBoneNames, FileName); UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); const bool bPreserveLocalTransform = FbxImporter->GetImportOptions()->bPreserveLocalTransform; // Build additional transform matrix UFbxAnimSequenceImportData* TemplateData = Cast<UFbxAnimSequenceImportData>(DestSeq->AssetImportData); FbxAMatrix FbxAddedMatrix; BuildFbxMatrixForImportTransform(FbxAddedMatrix, TemplateData); FMatrix AddedMatrix = Converter.ConvertMatrix(FbxAddedMatrix); const int32 NumSamplingKeys = FMath::FloorToInt(AnimTimeSpan.GetDuration().GetSecondDouble() * ResampleRate); const FbxTime TimeIncrement = (NumSamplingKeys > 1)? AnimTimeSpan.GetDuration() / (NumSamplingKeys - 1) : AnimTimeSpan.GetDuration(); for(int32 SourceTrackIdx = 0; SourceTrackIdx < FbxRawBoneNames.Num(); ++SourceTrackIdx) { int32 NumKeysForTrack = 0; // see if it's found in Skeleton FName BoneName = FbxRawBoneNames[SourceTrackIdx]; int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(BoneName); // update status FFormatNamedArguments Args; Args.Add(TEXT("TrackName"), FText::FromName(BoneName)); Args.Add(TEXT("TotalKey"), FText::AsNumber(NumSamplingKeys)); Args.Add(TEXT("TrackIndex"), FText::AsNumber(SourceTrackIdx+1)); Args.Add(TEXT("TotalTracks"), FText::AsNumber(FbxRawBoneNames.Num())); const FText StatusUpate = FText::Format(LOCTEXT("ImportingAnimTrackDetail", "Importing Animation Track [{TrackName}] ({TrackIndex}/{TotalTracks}) - TotalKey {TotalKey}"), Args); GWarn->StatusForceUpdate(SourceTrackIdx + 1, FbxRawBoneNames.Num(), StatusUpate); if (BoneTreeIndex!=INDEX_NONE) { bool bSuccess = true; FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); AnimationTransformDebug::FAnimationTransformDebugData NewDebugData; FbxNode* Link = SortedLinks[SourceTrackIdx]; FbxNode * LinkParent = Link->GetParent(); for(FbxTime CurTime = AnimTimeSpan.GetStart(); CurTime <= AnimTimeSpan.GetStop(); CurTime += TimeIncrement) { // save global trasnform FbxAMatrix GlobalMatrix = Link->EvaluateGlobalTransform(CurTime); // we'd like to verify this before going to Transform. // currently transform has tons of NaN check, so it will crash there FMatrix GlobalUEMatrix = Converter.ConvertMatrix(GlobalMatrix); if (GlobalUEMatrix.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidTransform", "Track {0} contains invalid transform. Could not import the track."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } FTransform GlobalTransform = Converter.ConvertTransform(GlobalMatrix); if (GlobalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealTransform", "Track {0} did not yeild valid transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } // debug data, including import transformation FTransform AddedTransform(AddedMatrix); NewDebugData.SourceGlobalTransform.Add(GlobalTransform * AddedTransform); FTransform LocalTransform; if( !bPreserveLocalTransform && LinkParent) { // I can't rely on LocalMatrix. I need to recalculate quaternion/scale based on global transform if Parent exists FbxAMatrix ParentGlobalMatrix = Link->GetParent()->EvaluateGlobalTransform(CurTime); FTransform ParentGlobalTransform = Converter.ConvertTransform(ParentGlobalMatrix); LocalTransform = GlobalTransform.GetRelativeTransform(ParentGlobalTransform); NewDebugData.SourceParentGlobalTransform.Add(ParentGlobalTransform); } else { FbxAMatrix& LocalMatrix = Link->EvaluateLocalTransform(CurTime); FbxVector4 NewLocalT = LocalMatrix.GetT(); FbxVector4 NewLocalS = LocalMatrix.GetS(); FbxQuaternion NewLocalQ = LocalMatrix.GetQ(); LocalTransform.SetTranslation(Converter.ConvertPos(NewLocalT)); LocalTransform.SetScale3D(Converter.ConvertScale(NewLocalS)); LocalTransform.SetRotation(Converter.ConvertRotToQuat(NewLocalQ)); NewDebugData.SourceParentGlobalTransform.Add(FTransform::Identity); } if(TemplateData && BoneTreeIndex == 0) { // If we found template data earlier, apply the import transform matrix to // the root track. LocalTransform.SetFromMatrix(LocalTransform.ToMatrixWithScale() * AddedMatrix); } if (LocalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealLocalTransform", "Track {0} did not yeild valid local transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); NewDebugData.RecalculatedLocalTransform.Add(LocalTransform); ++NumKeysForTrack; } if (bSuccess) { //add new track int32 NewTrackIdx = RawAnimationData.Add(RawTrack); DestSeq->AnimationTrackNames.Add(BoneName); NewDebugData.SetTrackData(NewTrackIdx, BoneTreeIndex, BoneName); // add mapping to skeleton bone track DestSeq->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(BoneTreeIndex)); TransformDebugData.Add(NewDebugData); } } TotalNumKeys = FMath::Max( TotalNumKeys, NumKeysForTrack ); } DestSeq->NumFrames = TotalNumKeys; GWarn->EndSlowTask(); } // compress animation { GWarn->BeginSlowTask( LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if(bSourceDataExists) { DestSeq->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress DestSeq->PostProcessSequence(); } // run debug mode AnimationTransformDebug::OutputAnimationTransformDebugData(TransformDebugData, TotalNumKeys, RefSkeleton); GWarn->EndSlowTask(); } return true; }
bool UnFbx::FFbxImporter::ValidateAnimStack(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, int32 ResampleRate, bool bImportMorph, FbxTimeSpan &AnimTimeSpan) { // set current anim stack Scene->SetCurrentAnimationStack(CurAnimStack); UE_LOG(LogFbx, Log, TEXT("Parsing AnimStack %s"),UTF8_TO_TCHAR(CurAnimStack->GetName())); // There are a FBX unroll filter bug, so don't bake animation layer at all MergeAllLayerAnimation(CurAnimStack, ResampleRate); bool bValidAnimStack = true; AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); // if no duration is found, return false if (AnimTimeSpan.GetDuration() <= 0) { return false; } const FBXImportOptions* ImportOption = GetImportOptions(); // only add morph time if not setrange. If Set Range there is no reason to override time if ( bImportMorph && ImportOption->AnimationLengthImportType != FBXALIT_SetRange) { for ( int32 NodeIndex = 0; NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { // Get the percentage of influence of the shape. FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { FbxTimeSpan TmpAnimSpan; if (Curve->GetTimeInterval(TmpAnimSpan)) { bValidAnimStack = true; // update animation interval to include morph target range AnimTimeSpan.UnionAssignment(TmpAnimSpan); } } } } } } } } return bValidAnimStack; }
bool UnFbx::FFbxImporter::IsValidAnimationData(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, int32& ValidTakeCount) { // If there are no valid links, then we cannot import the anim set if(SortedLinks.Num() == 0) { return false; } ValidTakeCount = 0; int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); int32 AnimStackIndex; for (AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++ ) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); // set current anim stack Scene->SetCurrentAnimationStack(CurAnimStack); // debug purpose for (int32 BoneIndex = 0; BoneIndex < SortedLinks.Num(); BoneIndex++) { FString BoneName = UTF8_TO_TCHAR(MakeName(SortedLinks[BoneIndex]->GetName())); UE_LOG(LogFbx, Log, TEXT("SortedLinks :(%d) %s"), BoneIndex, *BoneName ); } FbxTimeSpan AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); if (AnimTimeSpan.GetDuration() <= 0) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FBXImport_ZeroLength", "Animation Stack {0} does not contain any valid key. Try different time options when import."), FText::FromString(UTF8_TO_TCHAR(CurAnimStack->GetName())))), FFbxErrors::Animation_ZeroLength); continue; } ValidTakeCount++; { bool bBlendCurveFound = false; for ( int32 NodeIndex = 0; !bBlendCurveFound && NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { // Get the percentage of influence of the shape. FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { bBlendCurveFound = true; break; } } } } } } } } return ( ValidTakeCount != 0 ); }
Bone::Bone(FbxScene& scene) { //construct hierarchy *this = Bone { *(scene.GetRootNode()) }; std::map<std::string, std::pair<unsigned int, scm::math::mat4f> > bone_info{} ; unsigned num_bones = 0; for (unsigned int i = 0; i < scene.GetGeometryCount(); i++) { FbxGeometry* geo = scene.GetGeometry(i); if (geo->GetAttributeType() == FbxNodeAttribute::eMesh) { //check for skinning, use first skin deformer FbxSkin* skin; for (unsigned i = 0; i < geo->GetDeformerCount(); ++i) { FbxDeformer* defPtr = { geo->GetDeformer(i) }; if (defPtr->GetDeformerType() == FbxDeformer::eSkin) { skin = static_cast<FbxSkin*>(defPtr); break; } } if (!skin) { Logger::LOG_ERROR << "Mesh does not contain skin deformer" << std::endl; assert(false); } //one cluster corresponds to one bone for (unsigned i = 0; i < skin->GetClusterCount(); ++i) { FbxCluster* cluster = skin->GetCluster(i); FbxNode* node = cluster->GetLink(); if (!node) { Logger::LOG_ERROR << "associated node does not exist!" << std::endl; assert(false); } std::string bone_name(node->GetName()); //helper to check for matrix magnitude auto magnitude = [](scm::math::mat4f const & mat)->float { float mag = 0.0f; for (unsigned i = 0; i < 16; ++i) { mag += mat[i] * mat[i]; } return mag; } ; //reference pose of bone FbxAMatrix bind_transform; cluster->GetTransformLinkMatrix(bind_transform); //check if the clusters have an extra transformation FbxAMatrix cluster_transform; cluster->GetTransformMatrix(cluster_transform); if (magnitude(to_gua::mat4f(cluster_transform) - scm::math::mat4f::identity()) > 0.000000001f) { Logger::LOG_WARNING << "weight cluster of bone '" << bone_name << "' has transformation, animation will be skewed" << std::endl; } //add bone to list if it is not already included if (bone_info.find(bone_name) == bone_info.end()) { bone_info[bone_name] = std::make_pair( num_bones, to_gua::mat4f(bind_transform.Inverse() * cluster_transform)); ++num_bones; } } // traverse hierarchy and set accumulated values in the bone this->set_properties(bone_info); } } }