1935 lines
71 KiB
C++
1935 lines
71 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "UObject/WeakObjectPtr.h"
|
|
#include "Misc/SecureHash.h"
|
|
#include "Factories/FbxAnimSequenceImportData.h"
|
|
#include "Factories/FbxImportUI.h"
|
|
#include "Logging/TokenizedMessage.h"
|
|
#include "Factories/FbxStaticMeshImportData.h"
|
|
#include "Factories/FbxTextureImportData.h"
|
|
#include "Factories/FbxSceneImportFactory.h"
|
|
#include "Materials/MaterialInterface.h"
|
|
#include "MeshBuild.h"
|
|
#include "Algo/LevenshteinDistance.h"
|
|
|
|
class AActor;
|
|
class ACameraActor;
|
|
class ALight;
|
|
class Error;
|
|
class FSkeletalMeshImportData;
|
|
class UActorComponent;
|
|
class UAnimSequence;
|
|
class UFbxSkeletalMeshImportData;
|
|
class ULightComponent;
|
|
class UMaterial;
|
|
class UMaterialInstanceConstant;
|
|
class UPhysicsAsset;
|
|
class USkeletalMesh;
|
|
class USkeleton;
|
|
class UStaticMesh;
|
|
class UTexture;
|
|
struct FExpressionInput;
|
|
struct FRichCurve;
|
|
struct FStaticMaterial;
|
|
struct FSkeletalMaterial;
|
|
|
|
// Temporarily disable a few warnings due to virtual function abuse in FBX source files
|
|
#pragma warning( push )
|
|
|
|
#pragma warning( disable : 4263 ) // 'function' : member function does not override any base class virtual member function
|
|
#pragma warning( disable : 4264 ) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
|
|
|
|
// Include the fbx sdk header
|
|
// temp undef/redef of _O_RDONLY because kfbxcache.h (included by fbxsdk.h) does
|
|
// a weird use of these identifiers inside an enum.
|
|
#ifdef _O_RDONLY
|
|
#define TMP_UNFBX_BACKUP_O_RDONLY _O_RDONLY
|
|
#define TMP_UNFBX_BACKUP_O_WRONLY _O_WRONLY
|
|
#undef _O_RDONLY
|
|
#undef _O_WRONLY
|
|
#endif
|
|
|
|
//Robert G. : Packing was only set for the 64bits platform, but we also need it for 32bits.
|
|
//This was found while trying to trace a loop that iterate through all character links.
|
|
//The memory didn't match what the debugger displayed, obviously since the packing was not right.
|
|
#pragma pack(push,8)
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// _CRT_SECURE_NO_DEPRECATE is defined but is not enough to suppress the deprecation
|
|
// warning for vsprintf and stricmp in VS2010. Since FBX is able to properly handle the non-deprecated
|
|
// versions on the appropriate platforms, _CRT_SECURE_NO_DEPRECATE is temporarily undefined before
|
|
// including the FBX headers
|
|
|
|
// The following is a hack to make the FBX header files compile correctly under Visual Studio 2012 and Visual Studio 2013
|
|
#if _MSC_VER >= 1700
|
|
#define FBX_DLL_MSC_VER 1600
|
|
#endif
|
|
|
|
|
|
#endif // PLATFORM_WINDOWS
|
|
|
|
// FBX casts null pointer to a reference
|
|
THIRD_PARTY_INCLUDES_START
|
|
#include <fbxsdk.h>
|
|
THIRD_PARTY_INCLUDES_END
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
|
|
|
|
|
#ifdef TMP_UNFBX_BACKUP_O_RDONLY
|
|
#define _O_RDONLY TMP_FBX_BACKUP_O_RDONLY
|
|
#define _O_WRONLY TMP_FBX_BACKUP_O_WRONLY
|
|
#undef TMP_UNFBX_BACKUP_O_RDONLY
|
|
#undef TMP_UNFBX_BACKUP_O_WRONLY
|
|
#endif
|
|
|
|
#pragma warning( pop )
|
|
|
|
class FSkeletalMeshImportData;
|
|
class FSkelMeshOptionalImportData;
|
|
class ASkeletalMeshActor;
|
|
struct FbxSceneInfo;
|
|
struct FExistingStaticMeshData;
|
|
|
|
DECLARE_LOG_CATEGORY_EXTERN(LogFbx, Log, All);
|
|
|
|
#define DEBUG_FBX_NODE( Prepend, FbxNode ) FPlatformMisc::LowLevelOutputDebugStringf( TEXT("%s %s\n"), UTF8_TO_TCHAR(Prepend), UTF8_TO_TCHAR( FbxNode->GetName() ) )
|
|
|
|
#define FBX_METADATA_PREFIX TEXT("FBX.")
|
|
|
|
namespace UnFbx
|
|
{
|
|
|
|
UENUM()
|
|
enum EFBXReimportDialogReturnOption
|
|
{
|
|
FBXRDRO_Ok,
|
|
FBXRDRO_ResetToFbx,
|
|
FBXRDRO_Cancel,
|
|
FBXRDRO_MAX,
|
|
};
|
|
|
|
struct FBXImportOptions
|
|
{
|
|
// General options
|
|
bool bCanShowDialog;
|
|
bool bIsImportCancelable;
|
|
bool bImportScene;
|
|
bool bImportAsSkeletalGeometry;
|
|
bool bImportAsSkeletalSkinning;
|
|
bool bImportMaterials;
|
|
bool bInvertNormalMap;
|
|
bool bImportTextures;
|
|
bool bImportLOD;
|
|
bool bUsedAsFullName;
|
|
bool bConvertScene;
|
|
bool bForceFrontXAxis;
|
|
bool bConvertSceneUnit;
|
|
bool bRemoveNameSpace;
|
|
FVector ImportTranslation;
|
|
FRotator ImportRotation;
|
|
float ImportUniformScale;
|
|
EFBXNormalImportMethod NormalImportMethod;
|
|
EFBXNormalGenerationMethod::Type NormalGenerationMethod;
|
|
bool bComputeWeightedNormals;
|
|
bool bTransformVertexToAbsolute;
|
|
bool bBakePivotInVertex;
|
|
EFBXImportType ImportType;
|
|
// Static Mesh options
|
|
bool bCombineToSingle;
|
|
EVertexColorImportOption::Type VertexColorImportOption;
|
|
FColor VertexOverrideColor;
|
|
float DistanceFieldResolutionScale;
|
|
bool bRemoveDegenerates;
|
|
bool bBuildReversedIndexBuffer;
|
|
bool bBuildNanite;
|
|
bool bGenerateLightmapUVs;
|
|
bool bOneConvexHullPerUCX;
|
|
bool bAutoGenerateCollision;
|
|
FName StaticMeshLODGroup;
|
|
bool bImportStaticMeshLODs;
|
|
bool bAutoComputeLodDistances;
|
|
TArray<float> LodDistances;
|
|
int32 MinimumLodNumber;
|
|
int32 LodNumber;
|
|
// Material import options
|
|
class UMaterialInterface *BaseMaterial;
|
|
FString BaseColorName;
|
|
FString BaseDiffuseTextureName;
|
|
FString BaseEmissiveColorName;
|
|
FString BaseNormalTextureName;
|
|
FString BaseEmmisiveTextureName;
|
|
FString BaseSpecularTextureName;
|
|
FString BaseOpacityTextureName;
|
|
EMaterialSearchLocation MaterialSearchLocation;
|
|
//If true the materials will be reorder to follow the fbx order
|
|
bool bReorderMaterialToFbxOrder;
|
|
// Skeletal Mesh options
|
|
bool bImportMorph;
|
|
bool bImportVertexAttributes;
|
|
bool bImportAnimations;
|
|
bool bUpdateSkeletonReferencePose;
|
|
bool bResample;
|
|
int32 ResampleRate;
|
|
bool bSnapToClosestFrameBoundary;
|
|
bool bImportRigidMesh;
|
|
bool bUseT0AsRefPose;
|
|
bool bPreserveSmoothingGroups;
|
|
bool bKeepSectionsSeparate;
|
|
FOverlappingThresholds OverlappingThresholds;
|
|
bool bImportMeshesInBoneHierarchy;
|
|
bool bCreatePhysicsAsset;
|
|
UPhysicsAsset *PhysicsAsset;
|
|
bool bImportSkeletalMeshLODs;
|
|
// Animation option
|
|
USkeleton* SkeletonForAnimation;
|
|
EFBXAnimationLengthImportType AnimationLengthImportType;
|
|
FIntPoint AnimationRange;
|
|
FString AnimationName;
|
|
bool bPreserveLocalTransform;
|
|
bool bDeleteExistingMorphTargetCurves;
|
|
bool bImportCustomAttribute;
|
|
bool bDeleteExistingCustomAttributeCurves;
|
|
bool bDeleteExistingNonCurveCustomAttributes;
|
|
bool bImportBoneTracks;
|
|
bool bSetMaterialDriveParameterOnCustomAttribute;
|
|
bool bAddCurveMetadataToSkeleton;
|
|
bool bRemoveRedundantKeys;
|
|
bool bDoNotImportCurveWithZero;
|
|
bool bResetToFbxOnMaterialConflict;
|
|
TArray<FString> MaterialCurveSuffixes;
|
|
|
|
/** This allow to add a prefix to the material name when unreal material get created.
|
|
* This prefix can just modify the name of the asset for materials (i.e. TEXT("Mat"))
|
|
* This prefix can modify the package path for materials (i.e. TEXT("/Materials/")).
|
|
* Or both (i.e. TEXT("/Materials/Mat"))
|
|
*/
|
|
FName MaterialBasePath;
|
|
|
|
//This data allow to override some fbx Material(point by the uint64 id) with existing unreal material asset
|
|
TMap<uint64, class UMaterialInterface*> OverrideMaterials;
|
|
|
|
bool ShouldImportNormals() const
|
|
{
|
|
return NormalImportMethod == FBXNIM_ImportNormals || NormalImportMethod == FBXNIM_ImportNormalsAndTangents;
|
|
}
|
|
|
|
bool ShouldImportTangents() const
|
|
{
|
|
return NormalImportMethod == FBXNIM_ImportNormalsAndTangents;
|
|
}
|
|
|
|
void ResetForReimportAnimation()
|
|
{
|
|
bImportMorph = true;
|
|
AnimationLengthImportType = FBXALIT_ExportedTime;
|
|
}
|
|
|
|
static void ResetOptions(FBXImportOptions *OptionsToReset)
|
|
{
|
|
check(OptionsToReset != nullptr);
|
|
*OptionsToReset = FBXImportOptions();
|
|
}
|
|
};
|
|
|
|
#define INVALID_UNIQUE_ID 0xFFFFFFFFFFFFFFFF
|
|
|
|
class FFbxAnimCurveHandle
|
|
{
|
|
public:
|
|
enum CurveTypeDescription
|
|
{
|
|
Transform_Translation_X,
|
|
Transform_Translation_Y,
|
|
Transform_Translation_Z,
|
|
Transform_Rotation_X,
|
|
Transform_Rotation_Y,
|
|
Transform_Rotation_Z,
|
|
Transform_Scaling_X,
|
|
Transform_Scaling_Y,
|
|
Transform_Scaling_Z,
|
|
NotTransform,
|
|
};
|
|
|
|
FFbxAnimCurveHandle()
|
|
{
|
|
UniqueId = INVALID_UNIQUE_ID;
|
|
Name.Empty();
|
|
ChannelIndex = 0;
|
|
CompositeIndex = 0;
|
|
KeyNumber = 0;
|
|
AnimationTimeSecond = 0.0f;
|
|
AnimCurve = nullptr;
|
|
CurveType = NotTransform;
|
|
}
|
|
|
|
//Identity Data
|
|
uint64 UniqueId;
|
|
FString Name;
|
|
int32 ChannelIndex;
|
|
int32 CompositeIndex;
|
|
|
|
//Curve Information
|
|
int32 KeyNumber;
|
|
float AnimationTimeSecond;
|
|
|
|
//Pointer to the curve data
|
|
FbxAnimCurve* AnimCurve;
|
|
|
|
CurveTypeDescription CurveType;
|
|
};
|
|
|
|
class FFbxAnimPropertyHandle
|
|
{
|
|
public:
|
|
FFbxAnimPropertyHandle()
|
|
{
|
|
Name.Empty();
|
|
DataType = eFbxFloat;
|
|
}
|
|
|
|
FFbxAnimPropertyHandle(const FFbxAnimPropertyHandle &PropertyHandle)
|
|
{
|
|
Name = PropertyHandle.Name;
|
|
DataType = PropertyHandle.DataType;
|
|
CurveHandles = PropertyHandle.CurveHandles;
|
|
}
|
|
|
|
FString Name;
|
|
EFbxType DataType;
|
|
TArray<FFbxAnimCurveHandle> CurveHandles;
|
|
};
|
|
|
|
class FFbxAnimNodeHandle
|
|
{
|
|
public:
|
|
FFbxAnimNodeHandle()
|
|
{
|
|
UniqueId = INVALID_UNIQUE_ID;
|
|
Name.Empty();
|
|
AttributeUniqueId = INVALID_UNIQUE_ID;
|
|
AttributeType = FbxNodeAttribute::eUnknown;
|
|
}
|
|
|
|
FFbxAnimNodeHandle(const FFbxAnimNodeHandle &NodeHandle)
|
|
{
|
|
UniqueId = NodeHandle.UniqueId;
|
|
Name = NodeHandle.Name;
|
|
AttributeUniqueId = NodeHandle.AttributeUniqueId;
|
|
AttributeType = NodeHandle.AttributeType;
|
|
NodeProperties = NodeHandle.NodeProperties;
|
|
AttributeProperties = NodeHandle.AttributeProperties;
|
|
}
|
|
|
|
uint64 UniqueId;
|
|
FString Name;
|
|
TMap<FString, FFbxAnimPropertyHandle> NodeProperties;
|
|
|
|
uint64 AttributeUniqueId;
|
|
FbxNodeAttribute::EType AttributeType;
|
|
TMap<FString, FFbxAnimPropertyHandle> AttributeProperties;
|
|
};
|
|
|
|
class FFbxCurvesAPI
|
|
{
|
|
public:
|
|
FFbxCurvesAPI()
|
|
{
|
|
Scene = nullptr;
|
|
}
|
|
//Name API
|
|
UNREALED_API void GetAllNodeNameArray(TArray<FString> &AllNodeNames) const;
|
|
UNREALED_API void GetAnimatedNodeNameArray(TArray<FString> &AnimatedNodeNames) const;
|
|
UNREALED_API void GetNodeAnimatedPropertyNameArray(const FString &NodeName, TArray<FString> &AnimatedPropertyNames) const;
|
|
UNREALED_API void GetCustomStringPropertyArray(const FString& NodeName, TArray<TPair<FString, FString>>& CustomPropertyPairs) const;
|
|
|
|
UE_DEPRECATED(4.21, "Please use FRichCurve version instead to get tangent weight support")
|
|
UNREALED_API void GetCurveData(const FString& NodeName, const FString& PropertyName, int32 ChannelIndex, int32 CompositeIndex, FInterpCurveFloat& CurveData, bool bNegative) const;
|
|
|
|
//This one should be use only by the sequencer the key tangents data is transform to fit the expected data we have in the old matinee code
|
|
UNREALED_API void GetCurveDataForSequencer(const FString& NodeName, const FString& PropertyName, int32 ChannelIndex, int32 CompositeIndex, FRichCurve& RichCurve, bool bNegative, float Scale = 1.0f) const;
|
|
|
|
UNREALED_API void GetCurveData(const FString& NodeName, const FString& PropertyName, int32 ChannelIndex, int32 CompositeIndex, FRichCurve& CurveData, bool bNegative) const;
|
|
|
|
|
|
UNREALED_API void GetBakeCurveData(const FString& NodeName, const FString& PropertyName, int32 ChannelIndex, int32 CompositeIndex, TArray<float>& CurveData, float PeriodTime, float StartTime = 0.0f, float StopTime= -1.0f, bool bNegative = false, float Scale = 1.0f) const;
|
|
|
|
//Handle API
|
|
UNREALED_API void GetAllNodePropertyCurveHandles(const FString& NodeName, const FString& PropertyName, TArray<FFbxAnimCurveHandle> &PropertyCurveHandles) const;
|
|
UNREALED_API void GetCurveHandle(const FString& NodeName, const FString& PropertyName, int32 ChannelIndex, int32 CompositeIndex, FFbxAnimCurveHandle &CurveHandle) const;
|
|
|
|
UE_DEPRECATED(4.21, "Please use FRichCurve version instead to get tangent weight support")
|
|
UNREALED_API void GetCurveData(const FFbxAnimCurveHandle &CurveHandle, FInterpCurveFloat& CurveData, bool bNegative) const;
|
|
|
|
//This one should be use only by the sequencer the key tangents data is transform to fit the expected data we have in the old matinee code
|
|
UNREALED_API void GetCurveDataForSequencer(const FFbxAnimCurveHandle &CurveHandle, FRichCurve& RichCurve, bool bNegative, float Scale = 1.0f) const;
|
|
|
|
UNREALED_API void GetCurveData(const FFbxAnimCurveHandle &CurveHandle, FRichCurve& CurveData, bool bNegative, float Scale = 1.0f) const;
|
|
|
|
UNREALED_API void GetBakeCurveData(const FFbxAnimCurveHandle &CurveHandle, TArray<float>& CurveData, float PeriodTime, float StartTime = 0.0f, float StopTime = -1.0f, bool bNegative = false, float Scale = 1.0f) const;
|
|
|
|
UNREALED_API void GetConvertedNonTransformCurveData(const FString& NodeName, bool bUseSequencerCurve, float UniformScale, TMap<FName, FRichCurve>& OutCurves);
|
|
|
|
//Conversion API
|
|
UE_DEPRECATED(4.21, "Please use FRichCurve version instead to get tangent weight support")
|
|
UNREALED_API void GetConvertedTransformCurveData(const FString& NodeName, FInterpCurveFloat& TranslationX, FInterpCurveFloat& TranslationY, FInterpCurveFloat& TranslationZ,
|
|
FInterpCurveFloat& EulerRotationX, FInterpCurveFloat& EulerRotationY, FInterpCurveFloat& EulerRotationZ,
|
|
FInterpCurveFloat& ScaleX, FInterpCurveFloat& ScaleY, FInterpCurveFloat& ScaleZ,
|
|
FTransform& DefaultTransform) const;
|
|
|
|
UNREALED_API void GetConvertedTransformCurveData(const FString& NodeName, FRichCurve& TranslationX, FRichCurve& TranslationY, FRichCurve& TranslationZ,
|
|
FRichCurve& EulerRotationX, FRichCurve& EulerRotationY, FRichCurve& EulerRotationZ,
|
|
FRichCurve& ScaleX, FRichCurve& ScaleY, FRichCurve& ScaleZ,
|
|
FTransform& DefaultTransform, bool bUseSequencerCurve, float UniformScale = 1.0f) const;
|
|
|
|
FbxScene* Scene;
|
|
TMap<uint64, FFbxAnimNodeHandle> CurvesData;
|
|
TMap<uint64, FTransform> TransformData;
|
|
|
|
private:
|
|
EInterpCurveMode GetUnrealInterpMode(FbxAnimCurveKey FbxKey) const;
|
|
};
|
|
|
|
struct FbxMeshInfo
|
|
{
|
|
FString Name;
|
|
uint64 UniqueId;
|
|
int32 FaceNum;
|
|
int32 VertexNum;
|
|
bool bTriangulated;
|
|
int32 MaterialNum;
|
|
bool bIsSkelMesh;
|
|
FString SkeletonRoot;
|
|
int32 SkeletonElemNum;
|
|
FString LODGroup;
|
|
int32 LODLevel;
|
|
int32 MorphNum;
|
|
};
|
|
|
|
//Node use to store the scene hierarchy transform will be relative to the parent
|
|
struct FbxNodeInfo
|
|
{
|
|
FString ObjectName;
|
|
uint64 UniqueId;
|
|
FbxAMatrix Transform;
|
|
FbxVector4 RotationPivot;
|
|
FbxVector4 ScalePivot;
|
|
|
|
FString AttributeName;
|
|
uint64 AttributeUniqueId;
|
|
FString AttributeType;
|
|
|
|
FString ParentName;
|
|
uint64 ParentUniqueId;
|
|
};
|
|
|
|
struct FbxSceneInfo
|
|
{
|
|
// data for static mesh
|
|
int32 NonSkinnedMeshNum;
|
|
|
|
//data for skeletal mesh
|
|
int32 SkinnedMeshNum;
|
|
|
|
// common data
|
|
int32 TotalGeometryNum;
|
|
int32 TotalMaterialNum;
|
|
int32 TotalTextureNum;
|
|
|
|
TArray<FbxMeshInfo> MeshInfo;
|
|
TArray<FbxNodeInfo> HierarchyInfo;
|
|
|
|
/* true if it has animation */
|
|
bool bHasAnimation;
|
|
bool bHasAnimationOnSkeletalMesh;
|
|
double FrameRate;
|
|
double TotalTime;
|
|
|
|
void Reset()
|
|
{
|
|
NonSkinnedMeshNum = 0;
|
|
SkinnedMeshNum = 0;
|
|
TotalGeometryNum = 0;
|
|
TotalMaterialNum = 0;
|
|
TotalTextureNum = 0;
|
|
MeshInfo.Empty();
|
|
HierarchyInfo.Empty();
|
|
bHasAnimation = false;
|
|
bHasAnimationOnSkeletalMesh = false;
|
|
FrameRate = 0.0;
|
|
TotalTime = 0.0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* FBX basic data conversion class.
|
|
*/
|
|
class FFbxDataConverter
|
|
{
|
|
public:
|
|
static void SetJointPostConversionMatrix(FbxAMatrix ConversionMatrix) { JointPostConversionMatrix = ConversionMatrix; }
|
|
static const FbxAMatrix &GetJointPostConversionMatrix() { return JointPostConversionMatrix; }
|
|
|
|
static void SetAxisConversionMatrix(FbxAMatrix ConversionMatrix) { AxisConversionMatrix = ConversionMatrix; AxisConversionMatrixInv = ConversionMatrix.Inverse(); }
|
|
static const FbxAMatrix &GetAxisConversionMatrix() { return AxisConversionMatrix; }
|
|
static const FbxAMatrix &GetAxisConversionMatrixInv() { return AxisConversionMatrixInv; }
|
|
|
|
static UNREALED_API FVector ConvertPos(FbxVector4 Vector);
|
|
static UNREALED_API FVector ConvertDir(FbxVector4 Vector);
|
|
static UNREALED_API FRotator ConvertEuler(FbxDouble3 Euler);
|
|
static UNREALED_API FVector ConvertScale(FbxDouble3 Vector);
|
|
static UNREALED_API FVector ConvertScale(FbxVector4 Vector);
|
|
static UNREALED_API FRotator ConvertRotation(FbxQuaternion Quaternion);
|
|
static UNREALED_API FVector ConvertRotationToFVect(FbxQuaternion Quaternion, bool bInvertRot);
|
|
static UNREALED_API FQuat ConvertRotToQuat(FbxQuaternion Quaternion);
|
|
static UNREALED_API float ConvertDist(FbxDouble Distance);
|
|
static UNREALED_API bool ConvertPropertyValue(FbxProperty& FbxProperty, FProperty& UnrealProperty, union UPropertyValue& OutUnrealPropertyValue);
|
|
static UNREALED_API FTransform ConvertTransform(FbxAMatrix Matrix);
|
|
static UNREALED_API FMatrix ConvertMatrix(const FbxAMatrix& Matrix);
|
|
static UNREALED_API FbxAMatrix ConvertMatrix(const FMatrix& Matrix);
|
|
|
|
/*
|
|
* Convert fbx linear space color to sRGB FColor
|
|
*/
|
|
static UNREALED_API FColor ConvertColor(FbxDouble3 Color);
|
|
|
|
static UNREALED_API FbxVector4 ConvertToFbxPos(FVector Vector);
|
|
static UNREALED_API FbxVector4 ConvertToFbxRot(FVector Vector);
|
|
static UNREALED_API FbxVector4 ConvertToFbxScale(FVector Vector);
|
|
|
|
/*
|
|
* Convert sRGB FColor to fbx linear space color
|
|
*/
|
|
static UNREALED_API FbxDouble3 ConvertToFbxColor(FColor Color);
|
|
static UNREALED_API FbxString ConvertToFbxString(FName Name);
|
|
static UNREALED_API FbxString ConvertToFbxString(const FString& String);
|
|
|
|
// FbxCamera with no rotation faces X with Y-up while ours faces X with Z-up so add a -90 degrees roll to compensate
|
|
static FRotator GetCameraRotation() { return FRotator(0.f, 0.f, -90.f); }
|
|
|
|
// FbxLight with no rotation faces -Z while ours faces Y so add a 90 degrees pitch to compensate
|
|
static FRotator GetLightRotation() { return FRotator(0.f, 90.f, 0.f); }
|
|
|
|
private:
|
|
static UNREALED_API FbxAMatrix JointPostConversionMatrix;
|
|
static UNREALED_API FbxAMatrix AxisConversionMatrix;
|
|
static UNREALED_API FbxAMatrix AxisConversionMatrixInv;
|
|
};
|
|
|
|
FBXImportOptions* GetImportOptions( class FFbxImporter* FbxImporter, UFbxImportUI* ImportUI, bool bShowOptionDialog, bool bIsAutomated, const FString& FullPath, bool& OutOperationCanceled, bool& OutImportAll, bool bIsObjFormat, const FString& InFilename, bool bForceImportType = false, EFBXImportType ImportType = FBXIT_StaticMesh);
|
|
UNREALED_API void ApplyImportUIToImportOptions(UFbxImportUI* ImportUI, FBXImportOptions& InOutImportOptions);
|
|
|
|
struct FImportedMaterialData
|
|
{
|
|
public:
|
|
void AddImportedMaterial( const FbxSurfaceMaterial& FbxMaterial, UMaterialInterface& UnrealMaterial );
|
|
bool IsAlreadyImported( const FbxSurfaceMaterial& FbxMaterial, FName ImportedMaterialName ) const;
|
|
UMaterialInterface* GetUnrealMaterial( const FbxSurfaceMaterial& FbxMaterial ) const;
|
|
void Clear();
|
|
private:
|
|
/** Mapping of FBX material to Unreal material. Some materials in FBX have the same name so we use this map to determine if materials are unique */
|
|
TMap<const FbxSurfaceMaterial*, TWeakObjectPtr<UMaterialInterface> > FbxToUnrealMaterialMap;
|
|
TSet<FName> ImportedMaterialNames;
|
|
};
|
|
|
|
enum EFbxCreator
|
|
{
|
|
Blender,
|
|
Unknow
|
|
};
|
|
|
|
class FFbxHelper
|
|
{
|
|
public:
|
|
/**
|
|
* This function is use to compute the weight between two name.
|
|
*/
|
|
static float NameCompareWeight(const FString& A, const FString& B)
|
|
{
|
|
FString Longer = A;
|
|
FString Shorter = B;
|
|
if (A.Len() < B.Len())
|
|
{
|
|
Longer = B;
|
|
Shorter = A;
|
|
}
|
|
|
|
if (Longer.Compare(Shorter, ESearchCase::CaseSensitive) == 0)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
if (Longer.Compare(Shorter, ESearchCase::IgnoreCase) == 0)
|
|
{
|
|
return 0.98f;
|
|
}
|
|
// We do the contain so it is giving better result since often we compare thing like copy and paste string name
|
|
// we want to match: BackZ
|
|
// between: Paste_BackZ and BackX
|
|
// EditDistance for Paste_BackZ is 5/11 =0.45
|
|
// EditDistance for BackX is 4/5= 0.8
|
|
// The contains weight for Paste_BackZ is 0.98- (0.25*(1.0-5/11)) = 0.844
|
|
if (Longer.Contains(Shorter, ESearchCase::CaseSensitive))
|
|
{
|
|
return 0.98f - 0.25f*(1.0f - ((float)(Shorter.Len()) / (float)(Longer.Len())));
|
|
}
|
|
if (Longer.Contains(Shorter, ESearchCase::IgnoreCase))
|
|
{
|
|
return 0.96f - 0.25f*(1.0f - ((float)(Shorter.Len()) / (float)(Longer.Len())));
|
|
}
|
|
|
|
|
|
float LongerLength = (float)Longer.Len();
|
|
if (LongerLength == 0)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
return (LongerLength - Algo::LevenshteinDistance(Longer, Shorter)) / LongerLength;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Main FBX Importer class.
|
|
*/
|
|
class FFbxImporter
|
|
{
|
|
public:
|
|
~FFbxImporter();
|
|
/**
|
|
* Returns the importer singleton. It will be created on the first request.
|
|
*/
|
|
UNREALED_API static FFbxImporter* GetInstance(bool bDoNotCreate = false);
|
|
static void DeleteInstance();
|
|
|
|
/**
|
|
* Clear all data that need to be clear when we start importing a fbx file.
|
|
*/
|
|
void ClearAllCaches()
|
|
{
|
|
//Clear the mesh name cache use to ensure unique mesh name and avoid name clash
|
|
MeshNamesCache.Reset();
|
|
|
|
//this cache is use to prevent a node to be transform twice. it has to be reset everytime we
|
|
//read a new fbx file
|
|
TransformSettingsToFbxApply.Reset();
|
|
}
|
|
|
|
/**
|
|
* The asset tool have a filter mecanism for ImportAsset, return true if the asset can be imported, false otherwise
|
|
*/
|
|
bool CanImportClass(UClass* Class) const;
|
|
|
|
/**
|
|
* The asset tool have a filter mecanism for CreateAsset, return true if the asset can be created, false otherwise
|
|
*/
|
|
bool CanCreateClass(UClass* Class) const;
|
|
|
|
/**
|
|
* Detect if the FBX file has skeletal mesh model. If there is deformer definition, then there is skeletal mesh.
|
|
* In this function, we don't need to import the scene. But the open process is time-consume if the file is large.
|
|
*
|
|
* @param InFilename FBX file name.
|
|
* @return int32 -1 if parse failed; 0 if geometry ; 1 if there are deformers; 2 otherwise
|
|
*/
|
|
int32 GetImportType(const FString& InFilename);
|
|
|
|
/**
|
|
* Get detail infomation in the Fbx scene
|
|
*
|
|
* @param Filename Fbx file name
|
|
* @param SceneInfo return the scene info
|
|
* @return bool true if get scene info successfully
|
|
*/
|
|
bool GetSceneInfo(FString Filename, FbxSceneInfo& SceneInfo, bool bPreventMaterialNameClash = false);
|
|
|
|
/**
|
|
* Initialize Fbx file for import.
|
|
*
|
|
* @param Filename
|
|
* @param bParseStatistics
|
|
* @return bool
|
|
*/
|
|
bool OpenFile(FString Filename);
|
|
|
|
/*
|
|
Make sure the file header is read
|
|
*/
|
|
bool ReadHeaderFromFile(const FString& Filename, bool bPreventMaterialNameClash = false);
|
|
|
|
/**
|
|
* Import Fbx file.
|
|
*
|
|
* @param Filename
|
|
* @return bool
|
|
*/
|
|
bool ImportFile(FString Filename, bool bPreventMaterialNameClash = false);
|
|
|
|
/**
|
|
* Convert the scene from the current options.
|
|
* The scene will be converted to RH -Y or RH X depending if we force a front X axis or not
|
|
*/
|
|
void ConvertScene();
|
|
|
|
/**
|
|
* Attempt to load an FBX scene from a given filename.
|
|
*
|
|
* @param Filename FBX file name to import.
|
|
* @returns true on success.
|
|
*/
|
|
UNREALED_API bool ImportFromFile(const FString& Filename, const FString& Type, bool bPreventMaterialNameClash = false);
|
|
|
|
/**
|
|
* Prime the importer with an already existing scene object.
|
|
*
|
|
* @param Scene - The scene we want to load.
|
|
*/
|
|
UE_DEPRECATED(5.1, "Do not use this function.")
|
|
UNREALED_API void SetScene(FbxScene* InScene);
|
|
|
|
/**
|
|
* Retrieve the FBX loader's error message explaining its failure to read a given FBX file.
|
|
* Note that the message should be valid even if the parser is successful and may contain warnings.
|
|
*
|
|
* @ return TCHAR* the error message
|
|
*/
|
|
const TCHAR* GetErrorMessage() const
|
|
{
|
|
return *ErrorMessage;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the object inside the FBX scene from the name
|
|
*
|
|
* @param ObjectName Fbx object name
|
|
* @param Root Root node, retrieve from it
|
|
* @return FbxNode* Fbx object node
|
|
*/
|
|
FbxNode* RetrieveObjectFromName(const TCHAR* ObjectName, FbxNode* Root = NULL);
|
|
|
|
/**
|
|
* Find the first node containing a mesh attribute for the specified LOD index.
|
|
*
|
|
* @param NodeLodGroup The LOD group fbx node
|
|
* @param LodIndex The index of the LOD we search the mesh node
|
|
*/
|
|
FbxNode* FindLODGroupNode(FbxNode* NodeLodGroup, int32 LodIndex, FbxNode *NodeToFind = nullptr);
|
|
|
|
/**
|
|
* Find the all the node containing a mesh attribute for the specified LOD index.
|
|
*
|
|
* @param OutNodeInLod All the mesh node under the lod group
|
|
* @param NodeLodGroup The LOD group fbx node
|
|
* @param LodIndex The index of the LOD we search the mesh node
|
|
*/
|
|
UNREALED_API void FindAllLODGroupNode(TArray<FbxNode*> &OutNodeInLod, FbxNode* NodeLodGroup, int32 LodIndex);
|
|
|
|
/**
|
|
* Find the first parent node containing a eLODGroup attribute.
|
|
*
|
|
* @param ParentNode The node where to start the search.
|
|
*/
|
|
FbxNode *RecursiveFindParentLodGroup(FbxNode *ParentNode);
|
|
|
|
/**
|
|
* Creates a static mesh with the given name and flags, imported from within the FBX scene.
|
|
* @param InParent
|
|
* @param Node Fbx Node to import
|
|
* @param Name the Unreal Mesh name after import
|
|
* @param Flags
|
|
* @param InStaticMesh if LODIndex is not 0, this is the base mesh object. otherwise is NULL
|
|
* @param LODIndex LOD level to import to
|
|
*
|
|
* @returns UObject* the UStaticMesh object.
|
|
*/
|
|
UNREALED_API UStaticMesh* ImportStaticMesh(UObject* InParent, FbxNode* Node, const FName& Name, EObjectFlags Flags, UFbxStaticMeshImportData* ImportData, UStaticMesh* InStaticMesh = NULL, int LODIndex = 0, const FExistingStaticMeshData* ExistMeshDataPtr = nullptr);
|
|
|
|
/**
|
|
* Creates a static mesh from all the meshes in FBX scene with the given name and flags.
|
|
*
|
|
* @param InParent
|
|
* @param MeshNodeArray Fbx Nodes to import
|
|
* @param Name the Unreal Mesh name after import
|
|
* @param Flags
|
|
* @param InStaticMesh if LODIndex is not 0, this is the base mesh object. otherwise is NULL
|
|
* @param LODIndex LOD level to import to
|
|
* @param OrderedMaterialNames If not null, the original fbx ordered materials name will be use to reorder the section of the mesh we currently import
|
|
*
|
|
* @returns UObject* the UStaticMesh object.
|
|
*/
|
|
UNREALED_API UStaticMesh* ImportStaticMeshAsSingle(UObject* InParent, TArray<FbxNode*>& MeshNodeArray, const FName InName, EObjectFlags Flags, UFbxStaticMeshImportData* TemplateImportData, UStaticMesh* InStaticMesh, int LODIndex = 0, const FExistingStaticMeshData* ExistMeshDataPtr = nullptr);
|
|
|
|
/**
|
|
* Finish the import of the staticmesh after all LOD have been process (cannot be call before all LOD are imported). There is two main operation done by this function
|
|
* 1. Build the staticmesh render data
|
|
* 2. Reorder the material array to follow the fbx file material order
|
|
*/
|
|
UNREALED_API void PostImportStaticMesh(UStaticMesh* StaticMesh, TArray<FbxNode*>& MeshNodeArray, int32 LODIndex = 0);
|
|
|
|
static void UpdateStaticMeshImportData(UStaticMesh *StaticMesh, UFbxStaticMeshImportData* StaticMeshImportData);
|
|
static void UpdateSkeletalMeshImportData(USkeletalMesh *SkeletalMesh, UFbxSkeletalMeshImportData* SkeletalMeshImportData, int32 SpecificLod, TArray<FName> *ImportMaterialOriginalNameData, TArray<FImportMeshLodSectionsData> *ImportMeshLodData);
|
|
void ImportStaticMeshGlobalSockets( UStaticMesh* StaticMesh );
|
|
void ImportStaticMeshLocalSockets( UStaticMesh* StaticMesh, TArray<FbxNode*>& MeshNodeArray);
|
|
|
|
/*
|
|
* Add a GeneratedLOD to the staticmesh at the specified LOD index
|
|
*/
|
|
void AddStaticMeshSourceModelGeneratedLOD(UStaticMesh* StaticMesh, int32 LODIndex);
|
|
|
|
/**
|
|
* Return the node that match the mesh name. Return nullptr in case there is no match
|
|
*/
|
|
FbxNode* GetMeshNodesFromName(const FString& ReimportMeshName, TArray<FbxNode*>& FbxMeshArray);
|
|
|
|
/**
|
|
* re-import Unreal static mesh from updated Fbx file
|
|
* if the Fbx mesh is in LODGroup, the LOD of mesh will be updated
|
|
*
|
|
* @param Mesh the original Unreal static mesh object
|
|
* @return UObject* the new Unreal mesh object
|
|
*/
|
|
UStaticMesh* ReimportStaticMesh(UStaticMesh* Mesh, UFbxStaticMeshImportData* TemplateImportData);
|
|
|
|
/**
|
|
* re-import Unreal static mesh from updated scene Fbx file
|
|
* if the Fbx mesh is in LODGroup, the LOD of mesh will be updated
|
|
*
|
|
* @param Mesh the original Unreal static mesh object
|
|
* @return UObject* the new Unreal mesh object
|
|
*/
|
|
UStaticMesh* ReimportSceneStaticMesh(uint64 FbxNodeUniqueId, uint64 FbxMeshUniqueId, UStaticMesh* Mesh, UFbxStaticMeshImportData* TemplateImportData);
|
|
|
|
/**
|
|
* re-import Unreal skeletal mesh from updated Fbx file
|
|
* If the Fbx mesh is in LODGroup, the LOD of mesh will be updated.
|
|
* If the FBX mesh contains morph, the morph is updated.
|
|
* Materials, textures and animation attached in the FBX mesh will not be updated.
|
|
*
|
|
* @param Mesh the original Unreal skeletal mesh object
|
|
* @return UObject* the new Unreal mesh object
|
|
*/
|
|
USkeletalMesh* ReimportSkeletalMesh(USkeletalMesh* Mesh, UFbxSkeletalMeshImportData* TemplateImportData, uint64 SkeletalMeshFbxUID = 0xFFFFFFFFFFFFFFFF, TArray<FbxNode*> *OutSkeletalMeshArray = nullptr);
|
|
|
|
/**
|
|
* Creates a skeletal mesh from Fbx Nodes with the given name and flags, imported from within the FBX scene.
|
|
* These Fbx Nodes bind to same skeleton. We need to bind them to one skeletal mesh.
|
|
*
|
|
* @param InParent
|
|
* @param NodeArray Fbx Nodes to import
|
|
* @param Name the Unreal Mesh name after import
|
|
* @param Flags
|
|
* @param FbxShapeArray Fbx Morph objects.
|
|
* @param OutData - Optional import data to populate
|
|
* @param bCreateRenderData - Whether or not skeletal mesh rendering data will be created.
|
|
* @param OrderedMaterialNames If not null, the original fbx ordered materials name will be use to reorder the section of the mesh we currently import
|
|
*
|
|
* @return The USkeletalMesh object created
|
|
*/
|
|
|
|
class FImportSkeletalMeshArgs
|
|
{
|
|
public:
|
|
FImportSkeletalMeshArgs()
|
|
: InParent(nullptr)
|
|
, NodeArray()
|
|
, Name(NAME_None)
|
|
, Flags(RF_NoFlags)
|
|
, TemplateImportData(nullptr)
|
|
, LodIndex(0)
|
|
, FbxShapeArray(nullptr)
|
|
, OutData(nullptr)
|
|
, bCreateRenderData(true)
|
|
, OrderedMaterialNames(nullptr)
|
|
, ImportMaterialOriginalNameData(nullptr)
|
|
, ImportMeshSectionsData(nullptr)
|
|
, bMapMorphTargetToTimeZero(false)
|
|
{}
|
|
|
|
UObject* InParent;
|
|
TArray<FbxNode*> NodeArray;
|
|
FName Name;
|
|
EObjectFlags Flags;
|
|
UFbxSkeletalMeshImportData* TemplateImportData;
|
|
int32 LodIndex;
|
|
TArray<FbxShape*> *FbxShapeArray;
|
|
FSkeletalMeshImportData* OutData;
|
|
bool bCreateRenderData;
|
|
TArray<FName> *OrderedMaterialNames;
|
|
|
|
TArray<FName> *ImportMaterialOriginalNameData;
|
|
FImportMeshLodSectionsData *ImportMeshSectionsData;
|
|
bool bMapMorphTargetToTimeZero;
|
|
};
|
|
|
|
UNREALED_API USkeletalMesh* ImportSkeletalMesh(FImportSkeletalMeshArgs &ImportSkeletalMeshArgs);
|
|
|
|
/**
|
|
* Add to the animation set, the animations contained within the FBX scene, for the given skeletal mesh
|
|
*
|
|
* @param Skeleton Skeleton that the animation belong to
|
|
* @param SortedLinks skeleton nodes which are sorted
|
|
* @param Filename Fbx file name
|
|
* @param NodeArray node array of FBX meshes
|
|
*/
|
|
UAnimSequence* ImportAnimations(USkeleton* Skeleton, UObject* Outer, TArray<FbxNode*>& SortedLinks, const FString& Name, UFbxAnimSequenceImportData* TemplateImportData, TArray<FbxNode*>& NodeArray);
|
|
|
|
/**
|
|
* Get Animation Time Span - duration of the animation
|
|
*/
|
|
UNREALED_API FbxTimeSpan GetAnimationTimeSpan(FbxNode* RootNode, FbxAnimStack* AnimStack);
|
|
|
|
/**
|
|
* When we get exported time we call GetanimationInterval from fbx sdk and it return the layer 0 by default
|
|
* This function return the sum of all layer instead of just the layer 0.
|
|
*/
|
|
void GetAnimationIntervalMultiLayer(FbxNode* RootNode, FbxAnimStack* AnimStack, FbxTimeSpan& AnimTimeSpan);
|
|
|
|
/**
|
|
* Import one animation from CurAnimStack
|
|
*
|
|
* @param Skeleton Skeleton that the animation belong to
|
|
* @param DestSeq Sequence it's overwriting data to
|
|
* @param Filename Fbx file name (not whole path)
|
|
* @param SortedLinks skeleton nodes which are sorted
|
|
* @param NodeArray node array of FBX meshes
|
|
* @param CurAnimStack Animation Data
|
|
* @param ResampleRate Resample Rate for data
|
|
* @param AnimTimeSpan AnimTimeSpan
|
|
*/
|
|
bool ImportAnimation(USkeleton* Skeleton, UAnimSequence* DestSeq, const FString& FileName, TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, const int32 ResampleRate, const FbxTimeSpan AnimTimeSpan, const bool bReimport);
|
|
/**
|
|
* Calculate the global Sample Rate for all the nodes in the FbxAnimStack pass in parameter
|
|
*
|
|
* @param FbxAnimStack The anim stack we want to know the best sample rate
|
|
*/
|
|
int32 GetGlobalAnimStackSampleRate(FbxAnimStack* CurAnimStack);
|
|
/**
|
|
* Calculate Max Sample Rate - separate out of the original ImportAnimations
|
|
*
|
|
* @param SortedLinks skeleton nodes which are sorted
|
|
* @param NodeArray node array of FBX meshes
|
|
*/
|
|
int32 GetMaxSampleRate(TArray<FbxNode*>& SortedLinks);
|
|
/**
|
|
* Validate Anim Stack - multiple check for validating animstack
|
|
*
|
|
* @param SortedLinks skeleton nodes which are sorted
|
|
* @param NodeArray node array of FBX meshes
|
|
* @param CurAnimStack Animation Data
|
|
* @param ResampleRate Resample Rate for data
|
|
* @param AnimTimeSpan AnimTimeSpan
|
|
*/
|
|
bool ValidateAnimStack(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, int32 ResampleRate, bool bImportMorph, bool bSnapToClosestFrameBoundary, FbxTimeSpan &AnimTimeSpan);
|
|
|
|
/**
|
|
* Import Fbx Morph object for the Skeletal Mesh.
|
|
* In Fbx, morph object is a property of the Fbx Node.
|
|
*
|
|
* @param SkelMeshNodeArray - Fbx Nodes that the base Skeletal Mesh construct from
|
|
* @param BaseSkelMesh - base Skeletal Mesh
|
|
* @param LODIndex - LOD index
|
|
*/
|
|
UNREALED_API void ImportFbxMorphTarget(TArray<FbxNode*> &SkelMeshNodeArray, USkeletalMesh* BaseSkelMesh, int32 LODIndex, FSkeletalMeshImportData &BaseSkeletalMeshImportData, const bool bSkinControlPointToTimeZero);
|
|
|
|
/**
|
|
* Import LOD object for skeletal mesh
|
|
*
|
|
* @param InSkeletalMesh - LOD mesh object
|
|
* @param BaseSkeletalMesh - base mesh object
|
|
* @param DesiredLOD - LOD level
|
|
@param ReregisterAssociatedComponents - if NULL, just re-registers all SkinnedMeshComponents but if you set the specific components, will only re-registers those components
|
|
*/
|
|
UNREALED_API bool ImportSkeletalMeshLOD(USkeletalMesh* InSkeletalMesh, USkeletalMesh* BaseSkeletalMesh, int32 DesiredLOD, UFbxSkeletalMeshImportData* TemplateImportData = nullptr);
|
|
|
|
/**
|
|
* Empties the FBX scene, releasing its memory.
|
|
* Currently, we can't release KFbxSdkManager because Fbx Sdk2010.2 has a bug that FBX can only has one global sdkmanager.
|
|
* From Fbx Sdk2011, we can create multiple KFbxSdkManager, then we can release it.
|
|
*/
|
|
UNREALED_API void ReleaseScene();
|
|
|
|
/**
|
|
* If the node model is a collision model, then fill it into collision model list
|
|
*
|
|
* @param Node Fbx node
|
|
* @return true if the node is a collision model
|
|
*/
|
|
bool FillCollisionModelList(FbxNode* Node);
|
|
|
|
/**
|
|
* Import collision models for one static mesh if it has collision models
|
|
*
|
|
* @param StaticMesh - mesh object to import collision models
|
|
* @param NodeName - name of Fbx node that the static mesh constructed from
|
|
* @return return true if the static mesh has collision model and import successfully
|
|
*/
|
|
bool ImportCollisionModels(UStaticMesh* StaticMesh, const FbxString& NodeName);
|
|
|
|
//help
|
|
static FString MakeName(const ANSICHAR* name);
|
|
static FString MakeString(const ANSICHAR* Name);
|
|
FName MakeNameForMesh(FString InName, FbxObject* FbxObject);
|
|
|
|
// meshes
|
|
|
|
/**
|
|
* Get all Fbx skeletal mesh objects in the scene. these meshes are grouped by skeleton they bind to
|
|
*
|
|
* @param Node Root node to find skeletal meshes
|
|
* @param outSkelMeshArray return Fbx meshes they are grouped by skeleton
|
|
*/
|
|
UNREALED_API void FillFbxSkelMeshArrayInScene(FbxNode* Node, TArray< TArray<FbxNode*>* >& outSkelMeshArray, bool ExpandLOD, bool bCombineSkeletalMesh, bool bForceFindRigid = false);
|
|
|
|
/**
|
|
* Find FBX meshes that match Unreal skeletal mesh according to the bone of mesh
|
|
*
|
|
* @param FillInMesh Unreal skeletal mesh
|
|
* @param bExpandLOD flag that if expand FBX LOD group when get the FBX node
|
|
* @param OutFBXMeshNodeArray return FBX mesh nodes that match the Unreal skeletal mesh
|
|
*
|
|
* @return the root bone that bind to the FBX skeletal meshes
|
|
*/
|
|
FbxNode* FindFBXMeshesByBone(const FName& RootBoneName, bool bExpandLOD, TArray<FbxNode*>& OutFBXMeshNodeArray);
|
|
|
|
/**
|
|
* Get mesh count (including static mesh and skeletal mesh, except collision models) and find collision models
|
|
*
|
|
* @param Node Root node to find meshes
|
|
* @param bCountLODs Whether or not to count meshes in LOD groups
|
|
* @return int32 mesh count
|
|
*/
|
|
int32 GetFbxMeshCount(FbxNode* Node,bool bCountLODs, int32 &OutNumLODGroups );
|
|
|
|
/**
|
|
* Fill the collision models array by going through all mesh node recursively
|
|
*
|
|
* @param Node Root node to find collision meshes
|
|
*/
|
|
UNREALED_API void FillFbxCollisionMeshArray(FbxNode* Node);
|
|
|
|
/**
|
|
* Get all Fbx mesh objects
|
|
*
|
|
* @param Node Root node to find meshes
|
|
* @param outMeshArray return Fbx meshes
|
|
*/
|
|
UNREALED_API void FillFbxMeshArray(FbxNode* Node, TArray<FbxNode*>& outMeshArray, UnFbx::FFbxImporter* FFbxImporter);
|
|
|
|
/**
|
|
* Get all Fbx mesh objects not under a LOD group and all LOD group node
|
|
*
|
|
* @param Node Root node to find meshes
|
|
* @param outLODGroupArray return Fbx LOD group
|
|
* @param outMeshArray return Fbx meshes with no LOD group
|
|
*/
|
|
UNREALED_API void FillFbxMeshAndLODGroupArray(FbxNode* Node, TArray<FbxNode*>& outLODGroupArray, TArray<FbxNode*>& outMeshArray);
|
|
|
|
/**
|
|
* Returns if the passed FbxNode can be used as a skeleton bone in Unreal.
|
|
*
|
|
* @return bool
|
|
*/
|
|
bool IsUnrealBone(FbxNode* Link);
|
|
|
|
/**
|
|
* Returns if the passed FbxNode can be used as a transform attribute in Unreal.
|
|
*
|
|
* @return bool
|
|
*/
|
|
bool IsUnrealTransformAttribute(FbxNode* Link);
|
|
|
|
/**
|
|
* Fill FBX skeletons to OutSortedLinks recursively
|
|
*
|
|
* @param Link Fbx node of skeleton root
|
|
* @param OutSortedLinks
|
|
*/
|
|
void RecursiveBuildSkeleton(FbxNode* Link, TArray<FbxNode*>& OutSortedLinks);
|
|
|
|
/**
|
|
* Fill FBX skeletons to OutSortedLinks
|
|
*
|
|
* @param ClusterArray Fbx clusters of FBX skeletal meshes
|
|
* @param OutSortedLinks
|
|
*/
|
|
void BuildSkeletonSystem(TArray<FbxCluster*>& ClusterArray, TArray<FbxNode*>& OutSortedLinks);
|
|
|
|
/**
|
|
* Get Unreal skeleton root from the FBX skeleton node.
|
|
* Mesh and dummy can be used as skeleton.
|
|
*
|
|
* @param Link one FBX skeleton node
|
|
*/
|
|
FbxNode* GetRootSkeleton(FbxNode* Link);
|
|
|
|
/**
|
|
* Get the object of import options
|
|
*
|
|
* @return FBXImportOptions
|
|
*/
|
|
UNREALED_API FBXImportOptions* GetImportOptions() const;
|
|
|
|
/*
|
|
* This function show a dialog to let the user know what will be change in the skeleton if the fbx is imported
|
|
*/
|
|
static void ShowFbxSkeletonConflictWindow(USkeletalMesh *SkeletalMesh, USkeleton* Skeleton, ImportCompareHelper::FSkeletonCompareData& SkeletonCompareData);
|
|
|
|
template<typename TMaterialType>
|
|
static void PrepareAndShowMaterialConflictDialog(const TArray<TMaterialType>& CurrentMaterial, TArray<TMaterialType>& ResultMaterial, TArray<int32>& RemapMaterial, TArray<FName>& RemapMaterialName, bool bCanShowDialog, bool bIsPreviewDialog, bool bForceResetOnConflict, EFBXReimportDialogReturnOption& OutReturnOption);
|
|
/*
|
|
* This function show a dialog to let the user resolve the material conflict that arise when re-importing a mesh
|
|
*/
|
|
template<typename TMaterialType>
|
|
static void ShowFbxMaterialConflictWindow(const TArray<TMaterialType>& InSourceMaterials, const TArray<TMaterialType>& InResultMaterials, TArray<int32>& RemapMaterials, TArray<bool>& FuzzyRemapMaterials, EFBXReimportDialogReturnOption& OutReturnOption, bool bIsPreviewConflict = false);
|
|
|
|
/**
|
|
* Apply asset import settings for transform to an FBX node
|
|
*
|
|
* @param Node Node to apply transform settings too
|
|
* @param AssetData the asset data object to get transform data from
|
|
*/
|
|
void ApplyTransformSettingsToFbxNode(FbxNode* Node, UFbxAssetImportData* AssetData);
|
|
|
|
/**
|
|
* Remove asset import settings for transform to an FBX node
|
|
*
|
|
* @param Node Node to apply transform settings too
|
|
* @param AssetData the asset data object to get transform data from
|
|
*/
|
|
void RemoveTransformSettingsFromFbxNode(FbxNode* Node, UFbxAssetImportData* AssetData);
|
|
|
|
/**
|
|
* Populate the given matrix with the correct information for the asset data, in
|
|
* a format that matches FBX internals or without conversion
|
|
*
|
|
* @param OutMatrix The matrix to fill
|
|
* @param AssetData The asset data to extract the transform info from
|
|
*/
|
|
void BuildFbxMatrixForImportTransform(FbxAMatrix& OutMatrix, UFbxAssetImportData* AssetData);
|
|
|
|
/**
|
|
* Import FbxCurve to Curve
|
|
*/
|
|
static bool ImportCurve(const FbxAnimCurve* FbxCurve, FRichCurve& RichCurve, const FbxTimeSpan &AnimTimeSpan, const bool bNegative = false, const float ValueScale = 1.f, const bool bAutoSetTangents = true);
|
|
|
|
/**
|
|
* Merge all layers of one AnimStack to one layer.
|
|
*
|
|
* @param AnimStack AnimStack which layers will be merged
|
|
* @param ResampleRate resample rate for the animation
|
|
*/
|
|
void MergeAllLayerAnimation(FbxAnimStack* AnimStack, int32 ResampleRate);
|
|
|
|
/**
|
|
* Get the UObjects that have been created so far during the import process.
|
|
*/
|
|
const TArray<TWeakObjectPtr<UObject>>& GetCreatedObjects() const { return CreatedObjects; }
|
|
private:
|
|
|
|
/**
|
|
* This function fill the last imported Material name. Those named are used to reorder the mesh sections
|
|
* during a re-import. In case material names use the skinxx workflow the LastImportedMaterialNames array
|
|
* will be empty to let the system reorder the mesh sections with the skinxx workflow.
|
|
*
|
|
* @param LastImportedMaterialNames This array will be filled with the BaseSkelMesh Material original imported names
|
|
* @param BaseSkelMesh Skeletal mesh holding the last imported material names. If null the LastImportedMaterialNames will be empty;
|
|
* @param OrderedMaterialNames if not null, it will be used to fill the LastImportedMaterialNames array. except if the names are using the _skinxx workflow
|
|
*/
|
|
void FillLastImportMaterialNames(TArray<FName> &LastImportedMaterialNames, USkeletalMesh* BaseSkelMesh, TArray<FName> *OrderedMaterialNames);
|
|
|
|
/**
|
|
* Verify that all meshes are also reference by a fbx hierarchy node. If it found some Geometry
|
|
* not reference it will add a tokenized error.
|
|
*/
|
|
void ValidateAllMeshesAreReferenceByNodeAttribute();
|
|
|
|
/*
|
|
* Parse the fbx and create some LODGroup for matching LODX_ prefix
|
|
* Simply change the hierarchy to incorporate LODGroup and child all LODX_ prefix with the matching geometry name.
|
|
* The LODX_ prefix support X from 0 to 9
|
|
* The X parameter do not have to be continuous, but the LOD will be set in the correct order.
|
|
*/
|
|
void ConvertLodPrefixToLodGroup();
|
|
|
|
/**
|
|
* Recursive search for a node having a mesh attribute
|
|
*
|
|
* @param Node The node from which we start the search for the first node containing a mesh attribute
|
|
*/
|
|
FbxNode *RecursiveGetFirstMeshNode(FbxNode* Node, FbxNode* NodeToFind = nullptr);
|
|
|
|
/**
|
|
* Recursive search for a node having a mesh attribute
|
|
*
|
|
* @param Node The node from which we start the search for the first node containing a mesh attribute
|
|
*/
|
|
void RecursiveGetAllMeshNode(TArray<FbxNode *> &OutAllNode, FbxNode* Node);
|
|
|
|
/**
|
|
* ActorX plug-in can export mesh and dummy as skeleton.
|
|
* For the mesh and dummy in the skeleton hierarchy, convert them to FBX skeleton.
|
|
*
|
|
* @param Node root skeleton node
|
|
* @param SkelMeshes skeletal meshes that bind to this skeleton
|
|
* @param bImportNestedMeshes if true we will import meshes nested in bone hierarchies instead of converting them to bones
|
|
*/
|
|
void RecursiveFixSkeleton(FbxNode* Node, TArray<FbxNode*> &SkelMeshes, bool bImportNestedMeshes );
|
|
|
|
/**
|
|
* Get all Fbx skeletal mesh objects which are grouped by skeleton they bind to
|
|
*
|
|
* @param Node Root node to find skeletal meshes
|
|
* @param outSkelMeshArray return Fbx meshes they are grouped by skeleton
|
|
* @param SkeletonArray
|
|
* @param ExpandLOD flag of expanding LOD to get each mesh
|
|
*/
|
|
void RecursiveFindFbxSkelMesh(FbxNode* Node, TArray< TArray<FbxNode*>* >& outSkelMeshArray, TArray<FbxNode*>& SkeletonArray, bool ExpandLOD);
|
|
|
|
/**
|
|
* Get all Fbx rigid mesh objects which are grouped by skeleton hierarchy
|
|
*
|
|
* @param Node Root node to find skeletal meshes
|
|
* @param outSkelMeshArray return Fbx meshes they are grouped by skeleton hierarchy
|
|
* @param SkeletonArray
|
|
* @param ExpandLOD flag of expanding LOD to get each mesh
|
|
*/
|
|
void RecursiveFindRigidMesh(FbxNode* Node, TArray< TArray<FbxNode*>* >& outSkelMeshArray, TArray<FbxNode*>& SkeletonArray, bool ExpandLOD);
|
|
|
|
/**
|
|
* Import Fbx Morph object for the Skeletal Mesh. Each morph target import processing occurs in a different thread
|
|
*
|
|
* @param SkelMeshNodeArray - Fbx Nodes that the base Skeletal Mesh construct from
|
|
* @param BaseSkelMesh - base Skeletal Mesh
|
|
* @param LODIndex - LOD index of the skeletal mesh
|
|
* @param bSkinControlPointToTimeZero Use the pose at T0 to map the morph targets onto, since the base mesh was imported at that time, rather than the ref pose.
|
|
*/
|
|
void ImportMorphTargetsInternal( TArray<FbxNode*>& SkelMeshNodeArray, USkeletalMesh* BaseSkelMesh, int32 LODIndex, FSkeletalMeshImportData &BaseSkeletalMeshImportData, bool bMapMorphTargetToTimeZero);
|
|
|
|
/**
|
|
* sub-method called from ImportSkeletalMeshLOD method
|
|
*
|
|
* @param InSkeletalMesh - newly created mesh used as LOD
|
|
* @param BaseSkeletalMesh - the destination mesh object
|
|
* @param DesiredLOD - the LOD index to import into. A new LOD entry is created if one doesn't exist
|
|
*/
|
|
void InsertNewLODToBaseSkeletalMesh(USkeletalMesh* InSkeletalMesh, USkeletalMesh* BaseSkeletalMesh, int32 DesiredLOD, UFbxSkeletalMeshImportData* TemplateImportData);
|
|
|
|
/**
|
|
* Method used to verify if the geometry is valid. For example, if the bounding box is tiny we should warn
|
|
* @param StaticMesh - The imported static mesh which we'd like to verify
|
|
*/
|
|
void VerifyGeometry(UStaticMesh* StaticMesh);
|
|
|
|
/**
|
|
* Get the epsilon value, according to the import settings, below which two vertex positions should be considered coincident.
|
|
*/
|
|
float GetPointComparisonThreshold() const;
|
|
|
|
/**
|
|
* Get the epsilon value, according to the import settings, below which a triangle should be considered to have "zero area" and therefore be degenerate.
|
|
*/
|
|
float GetTriangleAreaThreshold() const;
|
|
|
|
/**
|
|
* When there is some materials with the same name we add a clash suffixe _ncl1_x.
|
|
* Example, if we have 3 materials name shader we will get (shader, shader_ncl1_1, shader_ncl1_2).
|
|
*/
|
|
void FixMaterialClashName();
|
|
|
|
/**
|
|
* Node with no name we will name it "ncl1_x" x is a a unique counter.
|
|
*/
|
|
void EnsureNodeNameAreValid(const FString& BaseFilename);
|
|
|
|
/**
|
|
* Remove metadata starting with the FBX_METADATA_PREFIX value associated to the specified UObject.
|
|
* This is used to ensure we do not restore old fbx meta data during reimports.
|
|
*
|
|
* @param Object The UObject for which we want the FBX metadata removed.
|
|
*/
|
|
static void RemoveFBXMetaData(const UObject* Object);
|
|
|
|
private:
|
|
/**
|
|
* Helper structure to pass around the common animation parameters.
|
|
*/
|
|
struct FAnimCurveImportSettings
|
|
{
|
|
FAnimCurveImportSettings(UAnimSequence* InDestSeq, const TArray<FbxNode*>& InNodeArray, const TArray<FbxNode*>& InSortedLinks, const TArray<FName>& InFbxRawBoneNames, const FbxTimeSpan& InAnimTimeSpan)
|
|
: DestSeq(InDestSeq)
|
|
, NodeArray(InNodeArray)
|
|
, SortedLinks(InSortedLinks)
|
|
, FbxRawBoneNames(InFbxRawBoneNames)
|
|
, AnimTimeSpan(InAnimTimeSpan)
|
|
{
|
|
}
|
|
|
|
UAnimSequence* DestSeq;
|
|
const TArray<FbxNode*>& NodeArray;
|
|
const TArray<FbxNode*>& SortedLinks;
|
|
const TArray<FName>& FbxRawBoneNames;
|
|
const FbxTimeSpan& AnimTimeSpan;
|
|
};
|
|
|
|
/**
|
|
* Import the blendshape curves into the UAnimSequence.
|
|
*
|
|
* @param AnimImportSettings Common settings to import animation.
|
|
* @param CurAnimStack The current anim stack we are importing.
|
|
* @param OutKeyCount Out parameter returning the number of keys imported, used to set the number of keys in the sequencer.
|
|
* @param bReimport Flag indicating whether or not this operation is part of reimporting.
|
|
*/
|
|
void ImportBlendShapeCurves(FAnimCurveImportSettings& AnimImportSettings, FbxAnimStack* CurAnimStack, int32& OutKeyCount, const bool bReimport);
|
|
|
|
/**
|
|
* Import the custom attributes curves into the UAnimSequence.
|
|
*
|
|
* @param AnimImportSettings Common settings to import animation.
|
|
* @param OutKeyCount Out parameter returning the number of keys imported, used to set the number of keys in the sequencer.
|
|
* @param OutCurvesNotFound Out parameter returning a list of curves name already present in the AnimSequence that were not imported, indicating that some curve data are missing during a reimport.
|
|
* @param bReimport Flag indicating whether or not this operation is part of reimporting.
|
|
*/
|
|
void ImportAnimationCustomAttribute(FAnimCurveImportSettings& AnimImportSettings, int32& OutKeyCount, TArray<FString>& OutCurvesNotFound, const bool bReimport);
|
|
|
|
/**
|
|
* Import the bone transforms curves into the UAnimSequence.
|
|
*
|
|
* @param Skeleton The skeleton for which we are currently importing the animations.
|
|
* @param AnimImportSettings Common settings to import animation.
|
|
* @param SkeletalMeshRootNode The fbx root node of the skeletalmesh.
|
|
* @param ResampleRate The rate at which the animations are resampled.
|
|
* @param OutTotalNumKeys Out parameter returning the number of keys imported, used to set the number of keys in the sequencer.
|
|
* @param bReimport Flag indicating whether or not this operation is part of reimporting.
|
|
*/
|
|
void ImportBoneTracks(USkeleton* Skeleton, FAnimCurveImportSettings& AnimImportSettings, FbxNode* SkeletalMeshRootNode, const int32 ResampleRate, int32& OutTotalNumKeys, const bool bReimport);
|
|
|
|
public:
|
|
// current Fbx scene we are importing. Make sure to release it after import
|
|
FbxScene* Scene;
|
|
FBXImportOptions* ImportOptions;
|
|
|
|
//We cache the hash of the file when we open the file. This is to avoid calculating the hash many time when importing many asset in one fbx file.
|
|
FMD5Hash Md5Hash;
|
|
|
|
struct FFbxMaterial
|
|
{
|
|
FbxSurfaceMaterial* FbxMaterial;
|
|
UMaterialInterface* Material;
|
|
|
|
FFbxMaterial()
|
|
: FbxMaterial(nullptr)
|
|
, Material(nullptr)
|
|
{}
|
|
|
|
FString GetName() const { return FbxMaterial ? UTF8_TO_TCHAR(FbxMaterial->GetName()) : (Material != nullptr ? Material->GetName() : TEXT("None")); }
|
|
};
|
|
|
|
FbxGeometryConverter* GetGeometryConverter() { return GeometryConverter; }
|
|
|
|
/*
|
|
* Cleanup the fbx file data so we can read again another file
|
|
*/
|
|
void PartialCleanUp();
|
|
|
|
FString GetFbxFileVersion() { return FbxFileVersion; }
|
|
FString GetFileCreator() { return FbxFileCreator; }
|
|
FString GetFileUnitSystem() { return FString(UTF8_TO_TCHAR(FileUnitSystem.GetScaleFactorAsString(false).Buffer())); }
|
|
FString GetFileAxisDirection();
|
|
|
|
protected:
|
|
enum IMPORTPHASE
|
|
{
|
|
NOTSTARTED,
|
|
FILEOPENED,
|
|
IMPORTED,
|
|
FIXEDANDCONVERTED,
|
|
};
|
|
|
|
/**
|
|
* Make material Unreal asset name from the Fbx material
|
|
*
|
|
* @param FbxMaterial Material from the Fbx node
|
|
* @return Sanitized asset name
|
|
*/
|
|
FString GetMaterialFullName(const FbxSurfaceMaterial& FbxMaterial) const;
|
|
|
|
FString GetMaterialBasePackageName(const FString& MaterialFullName) const;
|
|
|
|
static TSharedPtr<FFbxImporter> StaticInstance;
|
|
static TSharedPtr<FFbxImporter> StaticPreviewInstance;
|
|
|
|
//make sure we are not applying two time the option transform to the same node
|
|
TArray<FbxNode*> TransformSettingsToFbxApply;
|
|
|
|
// scene management
|
|
FFbxDataConverter Converter;
|
|
FbxGeometryConverter* GeometryConverter;
|
|
FbxManager* SdkManager;
|
|
FbxImporter* Importer;
|
|
IMPORTPHASE CurPhase;
|
|
FString ErrorMessage;
|
|
// base path of fbx file
|
|
FString FileBasePath;
|
|
TWeakObjectPtr<UObject> Parent;
|
|
FString FbxFileVersion;
|
|
FString FbxFileCreator;
|
|
|
|
//Original File Info
|
|
FbxAxisSystem FileAxisSystem;
|
|
FbxSystemUnit FileUnitSystem;
|
|
|
|
// Flag that the mesh is the first mesh to import in current FBX scene
|
|
// FBX scene may contain multiple meshes, importer can import them at one time.
|
|
// Initialized as true when start to import a FBX scene
|
|
bool bFirstMesh;
|
|
|
|
//Value is true if the file was create by blender
|
|
EFbxCreator FbxCreator;
|
|
|
|
// Set when importing skeletal meshes if the merge bones step fails. Used to track
|
|
// YesToAll and NoToAll for an entire scene
|
|
EAppReturnType::Type LastMergeBonesChoice;
|
|
|
|
/**
|
|
* A map holding the original name of the renamed fbx nodes,
|
|
* It is used namely to associate collision meshes to their corresponding static mesh if it has been renamed.
|
|
*/
|
|
FbxMap<FbxString, FbxString> NodeUniqueNameToOriginalNameMap;
|
|
|
|
/**
|
|
* A map holding pairs of fbx texture that needs to be renamed with the
|
|
* associated string to avoid name conflicts.
|
|
*/
|
|
TMap<FbxFileTexture*, FString> FbxTextureToUniqueNameMap;
|
|
|
|
/**
|
|
* Collision model list. The key is fbx node name
|
|
* If there is an collision model with old name format, the key is empty string("").
|
|
*/
|
|
FbxMap<FbxString, TSharedPtr< FbxArray<FbxNode* > > > CollisionModels;
|
|
|
|
TArray<TWeakObjectPtr<UObject>> CreatedObjects;
|
|
|
|
FFbxImporter();
|
|
|
|
|
|
/**
|
|
* Set up the static mesh data from Fbx Mesh.
|
|
*
|
|
* @param StaticMesh Unreal static mesh object to fill data into
|
|
* @param LODIndex LOD level to set up for StaticMesh
|
|
* @return bool true if set up successfully
|
|
*/
|
|
bool BuildStaticMeshFromGeometry(FbxNode* Node, UStaticMesh* StaticMesh, TArray<FFbxMaterial>& MeshMaterials, int LODIndex,
|
|
EVertexColorImportOption::Type VertexColorImportOption, const TMap<FVector3f, FColor>& ExistingVertexColorData, const FColor& VertexOverrideColor);
|
|
|
|
/**
|
|
* Clean up for destroy the Importer.
|
|
*/
|
|
void CleanUp();
|
|
|
|
/**
|
|
* Compute the global matrix for Fbx Node
|
|
* If we import scene it will return identity plus the pivot if we turn the bake pivot option
|
|
*
|
|
* @param Node Fbx Node
|
|
* @return KFbxXMatrix* The global transform matrix
|
|
*/
|
|
FbxAMatrix ComputeTotalMatrix(FbxNode* Node);
|
|
|
|
/**
|
|
* Compute the matrix for skeletal Fbx Node
|
|
* If we import don't import a scene it will call ComputeTotalMatrix with Node as the parameter. If we import a scene
|
|
* it will return the relative transform between the RootSkeletalNode and Node.
|
|
*
|
|
* @param Node Fbx Node
|
|
* @param Node Fbx RootSkeletalNode
|
|
* @return KFbxXMatrix* The global transform matrix
|
|
*/
|
|
FbxAMatrix ComputeSkeletalMeshTotalMatrix(FbxNode* Node, FbxNode *RootSkeletalNode);
|
|
/**
|
|
* Check if there are negative scale in the transform matrix and its number is odd.
|
|
* @return bool True if there are negative scale and its number is 1 or 3.
|
|
*/
|
|
bool IsOddNegativeScale(FbxAMatrix& TotalMatrix);
|
|
|
|
// various actors, current the Fbx importer don't importe them
|
|
/**
|
|
* Import Fbx light
|
|
*
|
|
* @param FbxLight fbx light object
|
|
* @param World in which to create the light
|
|
* @return ALight*
|
|
*/
|
|
ALight* CreateLight(FbxLight* InLight, UWorld* InWorld );
|
|
/**
|
|
* Import Light detail info
|
|
*
|
|
* @param FbxLight
|
|
* @param UnrealLight
|
|
* @return bool
|
|
*/
|
|
bool FillLightComponent(FbxLight* Light, ULightComponent* UnrealLight);
|
|
/**
|
|
* Import Fbx Camera object
|
|
*
|
|
* @param FbxCamera Fbx camera object
|
|
* @param World in which to create the camera
|
|
* @return ACameraActor*
|
|
*/
|
|
ACameraActor* CreateCamera(FbxCamera* InCamera, UWorld* InWorld);
|
|
|
|
// meshes
|
|
/**
|
|
* Fill skeletal mesh data from Fbx Nodes. If this function needs to triangulate the mesh, then it could invalidate the
|
|
* original FbxMesh pointer. Hence FbxMesh is a reference so this function can set the new pointer if need be.
|
|
*
|
|
* @param ImportData object to store skeletal mesh data
|
|
* @param FbxMesh Fbx mesh object belonging to Node
|
|
* @param FbxSkin Fbx Skin object belonging to FbxMesh
|
|
* @param FbxShape Fbx Morph object, if not NULL, we are importing a morph object.
|
|
* @param SortedLinks Fbx Links(bones) of this skeletal mesh
|
|
* @param FbxMatList All material names of the skeletal mesh
|
|
* @param RootNode The skeletal mesh root fbx node.
|
|
* @param ExistingVertexColorData Map of the existing vertex color data, used in the case we want to ignore the FBX vertex color during reimport.
|
|
*
|
|
* @returns bool* true if import successfully.
|
|
*/
|
|
bool FillSkelMeshImporterFromFbx(FSkeletalMeshImportData& ImportData, FbxMesh*& Mesh, FbxSkin* Skin,
|
|
FbxShape* Shape, TArray<FbxNode*> &SortedLinks, const TArray<FbxSurfaceMaterial*>& FbxMaterials, FbxNode *RootNode, const TMap<FVector3f, FColor>& ExistingVertexColorData);
|
|
public:
|
|
|
|
/**
|
|
* Fill FSkeletalMeshIMportData from Fbx Nodes and FbxShape Array if exists.
|
|
*
|
|
* @param NodeArray Fbx node array to look at
|
|
* @param TemplateImportData template import data
|
|
* @param FbxShapeArray Fbx Morph object, if not NULL, we are importing a morph object.
|
|
* @param OutData FSkeletalMeshImportData output data
|
|
* @param ExistingVertexColorData Map of the existing vertex color data, used in the case we want to ignore the FBX vertex color during reimport.
|
|
*
|
|
* @returns bool* true if import successfully.
|
|
*/
|
|
bool FillSkeletalMeshImportData(TArray<FbxNode*>& NodeArray, UFbxSkeletalMeshImportData* TemplateImportData, TArray<FbxShape*> *FbxShapeArray,
|
|
FSkeletalMeshImportData* OutData, TArray<FbxNode*>& OutImportedSkeletonLinkNodes, TArray<FName> &LastImportedMaterialNames,
|
|
const bool bIsReimport, const TMap<FVector3f, FColor>& ExistingVertexColorData, bool& bMapMorphTargetToTimeZero);
|
|
|
|
protected:
|
|
|
|
bool ReplaceSkeletalMeshGeometryImportData(const USkeletalMesh* SkeletalMesh, FSkeletalMeshImportData* ImportData, int32 LodIndex);
|
|
bool ReplaceSkeletalMeshSkinningImportData(const USkeletalMesh* SkeletalMesh, FSkeletalMeshImportData* ImportData, int32 LodIndex);
|
|
|
|
/**
|
|
* Fill the Points in FSkeletalMeshIMportData from a Fbx Node and a FbxShape if it exists.
|
|
*
|
|
* @param OutData FSkeletalMeshImportData output data
|
|
* @param RootNode The root node of the Fbx
|
|
* @param Node The node to get the points from
|
|
* @param FbxShape Fbx Morph object, if not NULL, we are importing a morph object.
|
|
*
|
|
* @returns bool true if import successfully.
|
|
*/
|
|
bool FillSkeletalMeshImportPoints(FSkeletalMeshImportData* OutData, FbxNode* RootNode, FbxNode* Node, FbxShape* FbxShape);
|
|
|
|
/**
|
|
* Fill the Points in FSkeletalMeshIMportData from Fbx Nodes and FbxShape Array if it exists.
|
|
*
|
|
* @param OutData FSkeletalMeshImportData output data
|
|
* @param NodeArray Fbx node array to look at
|
|
* @param FbxShapeArray Fbx Morph object, if not NULL, we are importing a morph object.
|
|
* @param ModifiedPoints Set of points indices for which we've modified the value in OutData
|
|
* @param bInUseT0AsRefPose Use the pose at T0 to map the morph targets onto, since the base mesh was imported at that time, rather than the ref pose.
|
|
*
|
|
* @returns bool true if import successfully.
|
|
*/
|
|
bool GatherPointsForMorphTarget(FSkeletalMeshImportData* OutData, TArray<FbxNode*>& NodeArray, TArray< FbxShape* >* FbxShapeArray, TSet<uint32>& ModifiedPoints, bool bSkinControlPointToTimeZero);
|
|
|
|
/**
|
|
* Import bones from skeletons that NodeArray bind to.
|
|
*
|
|
* @param NodeArray Fbx Nodes to import, they are bound to the same skeleton system
|
|
* @param ImportData object to store skeletal mesh data
|
|
* @param OutSortedLinks return all skeletons (bone nodes) sorted by depth traversal
|
|
* @param bOutDiffPose
|
|
* @param bDisableMissingBindPoseWarning
|
|
* @param bUseTime0AsRefPose in/out - Use Time 0 as Ref Pose
|
|
* @param SkeletalMeshNode A pointer to the skeletal mesh node used when we need to calculate the relative transform during scene import
|
|
* @param bIsReimport Are we reimporting
|
|
*/
|
|
bool ImportBones(TArray<FbxNode*>& NodeArray, FSkeletalMeshImportData &ImportData, UFbxSkeletalMeshImportData* TemplateData, TArray<FbxNode*> &OutSortedLinks, bool& bOutDiffPose, bool bDisableMissingBindPoseWarning, bool & bUseTime0AsRefPose, FbxNode *SkeletalMeshNode, bool bIsReimport);
|
|
|
|
/**
|
|
* Skins the control points of the given mesh or shape using either the default pose for skinning or the first frame of the
|
|
* default animation. The results are saved as the last X verts in the given FSkeletalMeshBinaryImport
|
|
*
|
|
* @param SkelMeshImporter object to store skeletal mesh data
|
|
* @param FbxMesh The Fbx mesh object with the control points to skin
|
|
* @param FbxShape If a shape (aka morph) is provided, its control points will be used instead of the given meshes
|
|
* @param bUseT0 If true, then the pose at time=0 will be used instead of the ref pose
|
|
*/
|
|
void SkinControlPointsToPose(FSkeletalMeshImportData &ImportData, FbxMesh* Mesh, FbxShape* Shape, bool bUseT0 );
|
|
|
|
// anims
|
|
/**
|
|
* Check if the Fbx node contains animation
|
|
*
|
|
* @param Node Fbx node
|
|
* @return bool true if the Fbx node contains animation.
|
|
*/
|
|
//bool IsAnimated(FbxNode* Node);
|
|
|
|
/**
|
|
* Fill each Trace for AnimSequence with Fbx skeleton animation by key
|
|
*
|
|
* @param Node Fbx skeleton node
|
|
* @param AnimSequence
|
|
* @param TakeName
|
|
* @param bIsRoot if the Fbx skeleton node is root skeleton
|
|
* @param Scale scale factor for this skeleton node
|
|
*/
|
|
bool FillAnimSequenceByKey(FbxNode* Node, UAnimSequence* AnimSequence, const char* TakeName, FbxTime& Start, FbxTime& End, bool bIsRoot, FbxVector4 Scale);
|
|
|
|
|
|
// material
|
|
/**
|
|
* Import each material Input from Fbx Material
|
|
*
|
|
* @param FbxMaterial Fbx material object
|
|
* @param UnrealMaterial
|
|
* @param MaterialProperty The material component to import
|
|
* @param MaterialInput
|
|
* @param bSetupAsNormalMap
|
|
* @param UVSet
|
|
* @return bool
|
|
*/
|
|
bool CreateAndLinkExpressionForMaterialProperty( const FbxSurfaceMaterial& FbxMaterial,
|
|
UMaterial* UnrealMaterial,
|
|
const char* MaterialProperty ,
|
|
FExpressionInput& MaterialInput,
|
|
bool bSetupAsNormalMap,
|
|
TArray<FString>& UVSet,
|
|
const FVector2D& Location );
|
|
/**
|
|
* Create and link texture to the right material parameter value
|
|
*
|
|
* @param FbxMaterial Fbx material object
|
|
* @param UnrealMaterial
|
|
* @param MaterialProperty The material component to import
|
|
* @param ParameterValue
|
|
* @param bSetupAsNormalMap
|
|
* @return bool
|
|
*/
|
|
bool LinkMaterialProperty(const FbxSurfaceMaterial& FbxMaterial,
|
|
UMaterialInstanceConstant* UnrealMaterial,
|
|
const char* MaterialProperty,
|
|
FName ParameterValue,
|
|
bool bSetupAsNormalMap);
|
|
/**
|
|
* Add a basic white diffuse color if no expression is linked to diffuse input.
|
|
*
|
|
* @param unMaterial Unreal material object.
|
|
*/
|
|
void FixupMaterial( const FbxSurfaceMaterial& FbxMaterial, UMaterial* unMaterial);
|
|
|
|
/**
|
|
* Get material mapping array according "Skinxx" flag in material name
|
|
*
|
|
* @param FSkeletalMeshBinaryImport& The unreal skeletal mesh.
|
|
*/
|
|
void SetMaterialSkinXXOrder(FSkeletalMeshImportData& ImportData);
|
|
|
|
void SetMaterialOrderByName(FSkeletalMeshImportData& ImportData, TArray<FName> LastImportedMaterialNames);
|
|
|
|
/**
|
|
* Make sure there is no unused material in the raw data. Unused material are material refer by node but not refer by any geometry face
|
|
*
|
|
* @param FSkeletalMeshBinaryImport& The unreal skeletal mesh.
|
|
*/
|
|
void CleanUpUnusedMaterials(FSkeletalMeshImportData& ImportData);
|
|
|
|
/**
|
|
* Create materials from Fbx node.
|
|
* Only setup channels that connect to texture, and setup the UV coordinate of texture.
|
|
* If diffuse channel has no texture, one default node will be created with constant.
|
|
* If a material cannot be imported a nullptr will be insterted in the outMaterials array in its place.
|
|
*
|
|
* @param FbxNode Fbx node
|
|
* @param outMaterials Unreal Materials we created
|
|
* @param UVSets UV set name list
|
|
* @return int32 material count that created from the Fbx node
|
|
*/
|
|
void FindOrImportMaterialsFromNode(FbxNode* FbxNode, TArray<UMaterialInterface*>& outMaterials, TArray<FString>& UVSets, bool bForSkeletalMesh);
|
|
|
|
/**
|
|
* Tries to find an existing UnrealMaterial from the FbxMaterial, returns nullptr if could not find a material.
|
|
* The function will look for materials imported by the FbxFactory first,
|
|
* and then search into the asset database using the passed MaterialSearchLocation search scope.
|
|
*
|
|
* @param FbxMaterial The FbxMaterial used to search the UnrealMaterial
|
|
* @param MaterialSearchLocation The asset database search scope.
|
|
* @return The UMaterialInterfaceFound, returns nullptr if no material was found.
|
|
*/
|
|
UMaterialInterface* FindExistingMaterialFromFbxMaterial(const FbxSurfaceMaterial& FbxMaterial, EMaterialSearchLocation MaterialSearchLocation);
|
|
|
|
/**
|
|
* Create Unreal material from Fbx material.
|
|
* Only setup channels that connect to texture, and setup the UV coordinate of texture.
|
|
* If diffuse channel has no texture, one default node will be created with constant.
|
|
*
|
|
* @param KFbxSurfaceMaterial* Fbx material
|
|
* @param outUVSets
|
|
* @param bForSkeletalMesh If set to true, the material target usage will be set to "SkeletalMesh".
|
|
* @return The created material.
|
|
*/
|
|
UMaterialInterface* CreateUnrealMaterial(const FbxSurfaceMaterial& FbxMaterial, TArray<FString>& OutUVSets, bool bForSkeletalMesh);
|
|
|
|
|
|
/**
|
|
* Visit all materials of one node, import textures from materials.
|
|
*
|
|
* @param Node FBX node.
|
|
*/
|
|
void ImportTexturesFromNode(FbxNode* Node);
|
|
|
|
/**
|
|
* Generate Unreal texture object from FBX texture.
|
|
*
|
|
* @param FbxTexture FBX texture object to import.
|
|
* @param bSetupAsNormalMap Flag to import this texture as normal map.
|
|
* @return UTexture* Unreal texture object generated.
|
|
*/
|
|
UTexture* ImportTexture(FbxFileTexture* FbxTexture, bool bSetupAsNormalMap);
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @param
|
|
* @return UMaterial*
|
|
*/
|
|
//UMaterial* GetImportedMaterial(KFbxSurfaceMaterial* pMaterial);
|
|
|
|
/**
|
|
* Check if the meshes in FBX scene contain smoothing group info.
|
|
* It's enough to only check one of mesh in the scene because "Export smoothing group" option affects all meshes when export from DCC.
|
|
* To ensure only check one time, use flag bFirstMesh to record if this is the first mesh to check.
|
|
*
|
|
* @param FbxMesh Fbx mesh to import
|
|
*/
|
|
void CheckSmoothingInfo(FbxMesh* FbxMesh);
|
|
|
|
/**
|
|
* check if two faces belongs to same smoothing group
|
|
*
|
|
* @param ImportData
|
|
* @param Face1 one face of the skeletal mesh
|
|
* @param Face2 another face
|
|
* @return bool true if two faces belongs to same group
|
|
*/
|
|
bool FacesAreSmoothlyConnected( FSkeletalMeshImportData &ImportData, int32 Face1, int32 Face2 );
|
|
|
|
/**
|
|
* Make un-smooth faces work.
|
|
*
|
|
* @param ImportData
|
|
* @return int32 number of points that added when process unsmooth faces
|
|
*/
|
|
int32 DoUnSmoothVerts(FSkeletalMeshImportData &ImportData, bool bDuplicateUnSmoothWedges = true);
|
|
|
|
/**
|
|
* Fill the FbxNodeInfo structure recursively to reflect the FbxNode hierarchy. The result will be an array sorted with the parent first
|
|
*
|
|
* @param SceneInfo The scene info to modify
|
|
* @param Parent The parent FbxNode
|
|
* @param ParentInfo The parent FbxNodeInfo
|
|
*/
|
|
void TraverseHierarchyNodeRecursively(FbxSceneInfo& SceneInfo, FbxNode *ParentNode, FbxNodeInfo &ParentInfo);
|
|
|
|
|
|
//
|
|
// for sequencer import
|
|
//
|
|
public:
|
|
UNREALED_API void PopulateAnimatedCurveData(FFbxCurvesAPI &CurvesAPI);
|
|
|
|
protected:
|
|
void LoadNodeKeyframeAnimationRecursively(FFbxCurvesAPI &CurvesAPI, FbxNode* NodeToQuery);
|
|
void LoadNodeKeyframeAnimation(FbxNode* NodeToQuery, FFbxCurvesAPI &CurvesAPI);
|
|
void SetupTransformForNode(FbxNode *Node);
|
|
|
|
/** Create a new asset from the package and objectname and class */
|
|
static UObject* CreateAssetOfClass(UClass* AssetClass, FString ParentPackageName, FString ObjectName, bool bAllowReplace = false);
|
|
|
|
/* Templated function to create an asset with given package and name */
|
|
template< class T>
|
|
static T* CreateAsset(FString ParentPackageName, FString ObjectName, bool bAllowReplace = false)
|
|
{
|
|
return (T*)CreateAssetOfClass(T::StaticClass(), ParentPackageName, ObjectName, bAllowReplace);
|
|
}
|
|
|
|
/**
|
|
* Fill up and verify bone names for animation
|
|
*/
|
|
void FillAndVerifyBoneNames(USkeleton* Skeleton, TArray<FbxNode*>& SortedLinks, TArray<FName> & OutRawBoneNames, FString Filename);
|
|
/**
|
|
* Is valid animation data
|
|
*/
|
|
bool IsValidAnimationData(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, int32& ValidTakeCount);
|
|
|
|
/**
|
|
* Retrieve pose array from bind pose
|
|
*
|
|
* Iterate through Scene:Poses, and find valid bind pose for NodeArray, and return those Pose if valid
|
|
*
|
|
*/
|
|
bool RetrievePoseFromBindPose(const TArray<FbxNode*>& NodeArray, FbxArray<FbxPose*> & PoseArray) const;
|
|
|
|
/** Import the user-defined properties on the node as FBX metadata on the object */
|
|
void ImportNodeCustomProperties(UObject* Object, FbxNode* Node, bool bPrefixTagWithNodeName = false);
|
|
|
|
public:
|
|
/** Import and set up animation related data from mesh **/
|
|
void SetupAnimationDataFromMesh(USkeletalMesh * SkeletalMesh, UObject* InParent, TArray<FbxNode*>& NodeArray, UFbxAnimSequenceImportData* ImportData, const FString& Filename);
|
|
|
|
/** error message handler */
|
|
UNREALED_API void AddTokenizedErrorMessage(TSharedRef<FTokenizedMessage> Error, FName FbxErrorName );
|
|
void ClearTokenizedErrorMessages();
|
|
void FlushToTokenizedErrorMessage(enum EMessageSeverity::Type Severity);
|
|
|
|
float GetOriginalFbxFramerate() { return OriginalFbxFramerate; }
|
|
|
|
/**
|
|
* Returns true if the last import operation was canceled.
|
|
*/
|
|
bool GetImportOperationCancelled() const { return bImportOperationCanceled; }
|
|
|
|
private:
|
|
friend class FFbxLoggerSetter;
|
|
friend struct FFbxScopedOperation;
|
|
|
|
// logger set/clear function
|
|
class FFbxLogger * Logger;
|
|
UNREALED_API void SetLogger(class FFbxLogger * InLogger);
|
|
UNREALED_API void ClearLogger();
|
|
|
|
FImportedMaterialData ImportedMaterialData;
|
|
|
|
//Cache to create unique name for mesh. This is use to fix name clash
|
|
TArray<FString> MeshNamesCache;
|
|
|
|
float OriginalFbxFramerate;
|
|
|
|
/**
|
|
* Holds if the current import operation was canceled or not.
|
|
*/
|
|
bool bImportOperationCanceled = false;
|
|
|
|
/**
|
|
* Internal counter used to group import operation together when canceling (ie: Import Skeletal Mesh can trigger Import of Morph Targets.)
|
|
*/
|
|
int32 ImportOperationStack = 0;
|
|
|
|
private:
|
|
|
|
/**
|
|
* Import FbxCurve to anim sequence
|
|
*/
|
|
bool ImportCurveToAnimSequence(class UAnimSequence * TargetSequence, const FString& CurveName, const FbxAnimCurve* FbxCurve, int32 CurveFlags,const FbxTimeSpan& AnimTimeSpan, const bool bReimport, float ValueScale = 1.f) const;
|
|
|
|
/**
|
|
* Import rich Curves to anim sequence
|
|
*/
|
|
bool ImportRichCurvesToAnimSequence(class UAnimSequence * TargetSequence, const TArray<FString>& CurveNames, const TArray<FRichCurve> RichCurves, int32 CurveFlags, const bool bReimport) const;
|
|
|
|
/**
|
|
* Given a primary blend shape channel curve and inbetween target full weights,
|
|
* generate curves for each target as if they are standalone blend shapes
|
|
* while preserving the animation
|
|
*/
|
|
TArray<FRichCurve> ResolveWeightsForBlendShapeCurve(FRichCurve& ChannelWeightCurve, const TArray<float>& InbetweenFullWeights) const;
|
|
|
|
/**
|
|
* Given a primary blend shape channel curve value and inbetween target full weights,
|
|
* calculate the curve value for each target as if they are standalone blend shapes
|
|
* while preserving the animation
|
|
*/
|
|
void ResolveWeightsForBlendShape(const TArray<float>& InbetweenFullWeights , float InWeight, float& OutMainWeight, TArray<float>& OutInbetweenWeights) const;
|
|
|
|
/**
|
|
* Import custom attribute (curve or not) to the associated bone.
|
|
*
|
|
* @return Returns true if the given custom attribute was properly added to the bone, false otherwise.
|
|
*/
|
|
bool ImportCustomAttributeToBone(class UAnimSequence* TargetSequence, FbxProperty& InProperty, FName BoneName, const FString& CurveName, const FbxAnimCurve* FbxCurve, const FbxTimeSpan& AnimTimeSpan, const bool bReimport, float ValueScale=1.f);
|
|
};
|
|
|
|
|
|
/** message Logger for FBX. Saves all the messages and prints when it's destroyed */
|
|
class FFbxLogger
|
|
{
|
|
UNREALED_API FFbxLogger();
|
|
UNREALED_API ~FFbxLogger();
|
|
|
|
/** Error messages **/
|
|
TArray<TSharedRef<FTokenizedMessage>> TokenizedErrorMessages;
|
|
|
|
/* The logger will show the LogMessage only if at least one TokenizedErrorMessage have a severity of Error or CriticalError*/
|
|
bool ShowLogMessageOnlyIfError;
|
|
|
|
friend class FFbxImporter;
|
|
friend class FFbxLoggerSetter;
|
|
};
|
|
|
|
/**
|
|
* This class is to make sure Logger isn't used by outside of purpose.
|
|
* We add this only top level of functions where it needs to be handled
|
|
* if the importer already has logger set, it won't set anymore
|
|
*/
|
|
class FFbxLoggerSetter
|
|
{
|
|
class FFbxLogger Logger;
|
|
FFbxImporter * Importer;
|
|
|
|
public:
|
|
FFbxLoggerSetter(FFbxImporter * InImpoter, bool ShowLogMessageOnlyIfError = false)
|
|
: Importer(InImpoter)
|
|
{
|
|
// if impoter doesn't have logger, sets it
|
|
if(Importer->Logger == NULL)
|
|
{
|
|
Logger.ShowLogMessageOnlyIfError = ShowLogMessageOnlyIfError;
|
|
Importer->SetLogger(&Logger);
|
|
}
|
|
else
|
|
{
|
|
// if impoter already has logger set
|
|
// invalidated Importer to make sure it doesn't clear
|
|
Importer = NULL;
|
|
}
|
|
}
|
|
|
|
~FFbxLoggerSetter()
|
|
{
|
|
if(Importer)
|
|
{
|
|
Importer->ClearLogger();
|
|
}
|
|
}
|
|
};
|
|
|
|
struct FFbxScopedOperation
|
|
{
|
|
public:
|
|
FFbxScopedOperation(FFbxImporter* FbxImporter);
|
|
~FFbxScopedOperation();
|
|
|
|
private:
|
|
FFbxImporter* Importer;
|
|
};
|
|
|
|
} // namespace UnFbx
|
|
|
|
|