void FAnimationRuntime::ExcludeBonesWithNoParents(const TArray<int32> & BoneIndices, const FReferenceSkeleton& RefSkeleton, TArray<int32> & FilteredRequiredBones)
{
	// Filter list, we only want bones that have their parents present in this array.
	FilteredRequiredBones.Empty(BoneIndices.Num());

	for (int32 Index=0; Index<BoneIndices.Num(); Index++)
	{
		const int32& BoneIndex = BoneIndices[Index];
		// Always add root bone.
		if( BoneIndex == 0 )
		{
			FilteredRequiredBones.Add(BoneIndex);
		}
		else
		{
			const int32 ParentBoneIndex = RefSkeleton.GetParentIndex(BoneIndex);
			if( FilteredRequiredBones.Contains(ParentBoneIndex) )
			{
				FilteredRequiredBones.Add(BoneIndex);
			}
			else
			{
				UE_LOG(LogAnimation, Warning, TEXT("ExcludeBonesWithNoParents: Filtering out bone (%s) since parent (%s) is missing"), 
					*RefSkeleton.GetBoneName(BoneIndex).ToString(), *RefSkeleton.GetBoneName(ParentBoneIndex).ToString());
			}
		}
	}
}
/**
* Process and fill in the mesh ref skeleton bone hierarchy using the raw binary import data
* 
* @param RefSkeleton - [out] reference skeleton hierarchy to update
* @param SkeletalDepth - [out] depth of the reference skeleton hierarchy
* @param ImportData - raw binary import data to process
* @return true if the operation completed successfully
*/
bool ProcessImportMeshSkeleton(FReferenceSkeleton& RefSkeleton, int32& SkeletalDepth, FSkeletalMeshImportData& ImportData)
{
	TArray <VBone>&	RefBonesBinary = ImportData.RefBonesBinary;

	// Setup skeletal hierarchy + names structure.
	RefSkeleton.Empty();

	// Digest bones to the serializable format.
	for( int32 b=0; b<RefBonesBinary.Num(); b++ )
	{
		const VBone & BinaryBone = RefBonesBinary[ b ];
		const FString BoneName = FSkeletalMeshImportData::FixupBoneName( BinaryBone.Name );
		const FMeshBoneInfo BoneInfo(FName(*BoneName, FNAME_Add, true), BinaryBone.ParentIndex);
		const FTransform BoneTransform(BinaryBone.BonePos.Orientation, BinaryBone.BonePos.Position, FVector(1.f));

		if(RefSkeleton.FindBoneIndex(BoneInfo.Name) != INDEX_NONE)
		{
			UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();
			FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("SkeletonHasDuplicateBones", "Skeleton has non-unique bone names.\nBone named '{0}' encountered more than once."), FText::FromName(BoneInfo.Name))));
			return false;
		}

		RefSkeleton.Add(BoneInfo, BoneTransform);
	}

	// Add hierarchy index to each bone and detect max depth.
	SkeletalDepth = 0;

	TArray<int32> SkeletalDepths;
	SkeletalDepths.Empty( RefBonesBinary.Num() );
	SkeletalDepths.AddZeroed( RefBonesBinary.Num() );
	for( int32 b=0; b < RefSkeleton.GetNum(); b++ )
	{
		int32 Parent	= RefSkeleton.GetParentIndex(b);
		int32 Depth	= 1.0f;

		SkeletalDepths[b]	= 1.0f;
		if( Parent != INDEX_NONE )
		{
			Depth += SkeletalDepths[Parent];
		}
		if( SkeletalDepth < Depth )
		{
			SkeletalDepth = Depth;
		}
		SkeletalDepths[b] = Depth;
	}

	return true;
}
	void OutputAnimationTransformDebugData(TArray<AnimationTransformDebug::FAnimationTransformDebugData> &TransformDebugData, int32 TotalNumKeys, const FReferenceSkeleton& RefSkeleton)
	{
		bool bShouldOutputToMessageLog = true;

		for(int32 Key=0; Key<TotalNumKeys; ++Key)
		{
			// go through all bones and find 
			for(int32 BoneIndex=0; BoneIndex<TransformDebugData.Num(); ++BoneIndex)
			{
				FAnimationTransformDebugData& Data = TransformDebugData[BoneIndex];
				int32 ParentIndex = RefSkeleton.GetParentIndex(Data.BoneIndex);
				int32 ParentTransformDebugDataIndex = 0;

				check(Data.RecalculatedLocalTransform.Num() == TotalNumKeys);
				check(Data.SourceGlobalTransform.Num() == TotalNumKeys);
				check(Data.SourceParentGlobalTransform.Num() == TotalNumKeys);

				for(; ParentTransformDebugDataIndex<BoneIndex; ++ParentTransformDebugDataIndex)
				{
					if(ParentIndex == TransformDebugData[ParentTransformDebugDataIndex].BoneIndex)
					{
						FTransform ParentTransform = TransformDebugData[ParentTransformDebugDataIndex].RecalculatedLocalTransform[Key] * TransformDebugData[ParentTransformDebugDataIndex].RecalculatedParentTransform[Key];
						Data.RecalculatedParentTransform.Add(ParentTransform);
						break;
					}
				}

				// did not find Parent
				if(ParentTransformDebugDataIndex == BoneIndex)
				{
					Data.RecalculatedParentTransform.Add(FTransform::Identity);
				}

				check(Data.RecalculatedParentTransform.Num() == Key+1);

				FTransform GlobalTransform = Data.RecalculatedLocalTransform[Key] * Data.RecalculatedParentTransform[Key];
				// makes more generous on the threshold. 
				if(GlobalTransform.Equals(Data.SourceGlobalTransform[Key], 0.1f) == false)
				{
					// so that we don't spawm with this message
					if(bShouldOutputToMessageLog)
					{
						UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();
						// now print information - it doesn't match well, find out what it is
						FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FBXImport_TransformError", "Imported bone transform is different from original. Please check Output Log to see detail of error. "),
							FText::FromName(Data.BoneName), FText::AsNumber(Data.BoneIndex), FText::FromString(Data.SourceGlobalTransform[Key].ToString()), FText::FromString(GlobalTransform.ToString()))), FFbxErrors::Animation_TransformError);

						bShouldOutputToMessageLog = false;
					}
					
					// now print information - it doesn't match well, find out what it is
					UE_LOG(LogFbx, Warning, TEXT("IMPORT TRANSFORM ERROR : Bone (%s:%d) \r\nSource Global Transform (%s), \r\nConverted Global Transform (%s)"),
						*Data.BoneName.ToString(), Data.BoneIndex, *Data.SourceGlobalTransform[Key].ToString(), *GlobalTransform.ToString());
				}
			}
		}
	}
void FBoneHierarchyBuilder::CopyToRefSkeleton(FReferenceSkeleton& RefSkeleton)
{
	for (int32 SourceBoneIndex = 0; SourceBoneIndex < AllBones.Num(); ++SourceBoneIndex)
	{
		const FName BoneName(AllBones[SourceBoneIndex]);
		const int32 ParentIndex(ParentIndices[SourceBoneIndex]);
		const FMeshBoneInfo BoneInfo(BoneName, BoneName.ToString(), ParentIndex);
		const FTransform& Transform = Transforms[SourceBoneIndex];
		RefSkeleton.Add(BoneInfo, Transform);
	}
}
/** Check that root bone is the same, and that any bones that are common have the correct parent. */
bool SkeletonsAreCompatible( const FReferenceSkeleton& NewSkel, const FReferenceSkeleton& ExistSkel )
{
	if(NewSkel.GetBoneName(0) != ExistSkel.GetBoneName(0))
	{
		UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();	
		FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("MeshHasDifferentRoot", "Root Bone is '{0}' instead of '{1}'.\nDiscarding existing LODs."),
			FText::FromName(NewSkel.GetBoneName(0)), FText::FromName(ExistSkel.GetBoneName(0)))));
		return false;
	}

	for(int32 i=1; i<NewSkel.GetNum(); i++)
	{
		// See if bone is in both skeletons.
		int32 NewBoneIndex = i;
		FName NewBoneName = NewSkel.GetBoneName(NewBoneIndex);
		int32 BBoneIndex = ExistSkel.FindBoneIndex(NewBoneName);

		// If it is, check parents are the same.
		if(BBoneIndex != INDEX_NONE)
		{
			FName NewParentName = NewSkel.GetBoneName( NewSkel.GetParentIndex(NewBoneIndex) );
			FName ExistParentName = ExistSkel.GetBoneName( ExistSkel.GetParentIndex(BBoneIndex) );

			if(NewParentName != ExistParentName)
			{
				UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();			
				FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("MeshHasDifferentRoot", "Root Bone is '{0}' instead of '{1}'.\nDiscarding existing LODs."),
					FText::FromName(NewBoneName), FText::FromName(NewParentName))));
				return false;
			}
		}
	}

	return true;
}