Skeletal meshes
Skeletal Meshes are made up of two parts: A set of polygons composed to make up the surface of the Skeletal Mesh, and a hierarchical set of interconnected bones which can be used to animate the vertices of the polygons.
They are often used to represent characters or other animating objects. The 3D models, rigging and animations are created in an external modeling and animation application (3DSMax, Maya, Softimage, etc), and are then imported into Unreal Engine and saved into packages by using Unreal Editor's Content Browser.
Import Skeletal Mesh
We work in an inherited class from UBlueprintFunctionLibrary
, thus our header file should look like below:
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
UCLASS()
class UExampleClass : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/**
* Editor Only - Will not work in packaged build.
*
* Create and process a SkeletalMesh import task.
*
* @param SourcePath The path of the source file: "C:/Temp/MySkeletalMesh.fbx"
* @param DestinationPath The path of the imported asset: "/Game/Folder/MySkeletalMesh"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The imported SkeletalMesh
*/
UFUNCTION(BlueprintCallable, Category = " ... ")
static class USkeletalMesh* ImportSkeletalMesh(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg);
};
#include "Factories/FbxImportUI.h" // UnrealEd (Editor Only)
#include "Factories/FbxSkeletalMeshImportData.h" // UnrealEd (Editor Only)
USkeletalMesh* UExampleCLass::ImportSkeletalMesh(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg)
{
UFbxImportUI* Options = NewObject<UFbxImportUI>();
// Required options to specify the skeletal mesh import
Options->bAutomatedImportShouldDetectType = false;
Options->MeshTypeToImport = EFBXImportType::FBXIT_SkeletalMesh;
Options->bImportMesh = true;
Options->bImportAsSkeletal = true;
Options->bCreatePhysicsAsset = true;
// More options
Options->bImportAnimations = false;
Options->bImportTextures = true;
Options->bImportMaterials = true;
Options->bResetToFbxOnMaterialConflict = true;
Options->LodNumber = 0;
// UFbxAssetImportData
Options->SkeletalMeshImportData->ImportTranslation = FVector(0.0f);
Options->SkeletalMeshImportData->ImportRotation = FRotator(0.0f);
Options->SkeletalMeshImportData->ImportUniformScale = 1.0f;
Options->SkeletalMeshImportData->bConvertScene = true;
Options->SkeletalMeshImportData->bForceFrontXAxis = true;
Options->SkeletalMeshImportData->bConvertSceneUnit = true;
// UFbxMeshImportData
Options->SkeletalMeshImportData->bTransformVertexToAbsolute = false;
Options->SkeletalMeshImportData->bBakePivotInVertex = false;
Options->SkeletalMeshImportData->bImportMeshLODs = true;
Options->SkeletalMeshImportData->NormalImportMethod = EFBXNormalImportMethod::FBXNIM_ComputeNormals;
Options->SkeletalMeshImportData->NormalGenerationMethod = EFBXNormalGenerationMethod::BuiltIn;
Options->SkeletalMeshImportData->bComputeWeightedNormals = true;
Options->SkeletalMeshImportData->bReorderMaterialToFbxOrder = false;
// UFbxSkeletalMeshImportData
Options->SkeletalMeshImportData->ImportContentType = EFBXImportContentType::FBXICT_All;
Options->SkeletalMeshImportData->VertexColorImportOption = EVertexColorImportOption::Replace;
Options->SkeletalMeshImportData->bUpdateSkeletonReferencePose = true;
Options->SkeletalMeshImportData->bUseT0AsRefPose = true;
Options->SkeletalMeshImportData->bPreserveSmoothingGroups = true;
Options->SkeletalMeshImportData->bImportMeshesInBoneHierarchy = true;
Options->SkeletalMeshImportData->bImportMorphTargets = true;
Options->SkeletalMeshImportData->ThresholdPosition = 0.0f;
Options->SkeletalMeshImportData->ThresholdTangentNormal = 0.0f;
Options->SkeletalMeshImportData->ThresholdUV = 0.0f;
Options->SkeletalMeshImportData->MorphThresholdPosition = 0.0f;
// Create the import task
UAssetImportTask* Task = UExampleClass::CreateImportTask(SourcePath, DestinationPath, nullptr, Options, bOutSuccess, OutInfoMsg);
if (!bOutSuccess) return nullptr;
// Import the asset
USkeletalMesh* RetAsset = Cast<USkeletalMesh>(UExampleClass::ProcessImportTask(Task, bOutSuccess, OutInfoMsg));
if (!bOutSuccess) return nullptr;
// Return the imported asset
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Import Skeletal Mesh Succeeded - '%s'"), *DestinationPath);
return RetAsset;
}
Within the UExampleClass::ImportAsset
, we link at 2 help custom functions - CreateImportTask
and ProcessImportTask
that can be defined within the same UExampleClass
.
CreateImportTask
function has numerous parameters while the 4th is Options
. We can use this parameter to set .fbx
file import pipeline, see the example below.
Importing assets using UBlueprintFunctionLibrary
class that works in editor mode only. While we use it in the editor, we need to place an additional module into project's .Build.cs
file. However, as it's editor only, those modules must be removed from the .Build.cs file before packaging the project (e.g. building the Windows app).
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// New Modules - Editor Only
"UnrealEd",
}
);
Import animation for Skeletal Mesh
/**
* Editor Only - Will not work in packaged build.
*
* Create and process a Animation import task.
*
* @param SourcePath The path of the source file: "C:/Temp/MyAnimation.fbx"
* @param DestinationPath The path of the imported asset: "/Game/Folder/MyAnimation"
* @param SkeletonPath The path of the the skeleton to use: "/Game/Folder/MySkeleton"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The imported Animation
*/
UFUNCTION(BlueprintCallable, Category = " ... ")
static class UAnimSequence* ImportAnimation(FString SourcePath, FString DestinationPath, FString SkeletonPath, bool& bOutSuccess, FString& OutInfoMsg);
#include "Factories/FbxImportUI.h" // UnrealEd (Editor Only)
#include "Factories/FbxAnimSequenceImportData.h" // UnrealEd (Editor Only)
UAnimSequence* UExampleClass::ImportAnimation(FString SourcePath, FString DestinationPath, FString SkeletonPath, bool& bOutSuccess, FString& OutInfoMsg)
{
UFbxImportUI* Options = NewObject<UFbxImportUI>();
// Required options to specify that we're importing an animation
Options->bAutomatedImportShouldDetectType = false;
Options->MeshTypeToImport = EFBXImportType::FBXIT_Animation;
Options->bImportAnimations = true;
Options->Skeleton = Cast<USkeleton>(StaticLoadObject(UObject::StaticClass(), nullptr, *SkeletonPath));
if (Options->Skeleton == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Import Animation Failed - Skeleton is invalid: '%s'"), *SkeletonPath);
return nullptr;
}
// Animation only, don't import anything else
Options->bImportMesh = false;
Options->bImportAsSkeletal = false;
Options->bCreatePhysicsAsset = false;
Options->bImportTextures = false;
Options->bImportMaterials = false;
Options->bResetToFbxOnMaterialConflict = true;
Options->LodNumber = 0;
Options->OverrideAnimationName = "";
// UFbxAssetImportData
Options->AnimSequenceImportData->ImportTranslation = FVector(0.0f);
Options->AnimSequenceImportData->ImportRotation = FRotator(0.0f);
Options->AnimSequenceImportData->ImportUniformScale = 1.0f;
Options->AnimSequenceImportData->bConvertScene = true;
Options->AnimSequenceImportData->bForceFrontXAxis = true;
Options->AnimSequenceImportData->bConvertSceneUnit = true;
// UFbxAnimSequenceImportData
Options->AnimSequenceImportData->bImportMeshesInBoneHierarchy = false;
Options->AnimSequenceImportData->AnimationLength = EFBXAnimationLengthImportType::FBXALIT_ExportedTime;
Options->AnimSequenceImportData->FrameImportRange = FInt32Interval();
Options->AnimSequenceImportData->bUseDefaultSampleRate = false;
Options->AnimSequenceImportData->CustomSampleRate = 0;
Options->AnimSequenceImportData->bSnapToClosestFrameBoundary = false;
Options->AnimSequenceImportData->bImportCustomAttribute = false;
Options->AnimSequenceImportData->bDeleteExistingCustomAttributeCurves = false;
Options->AnimSequenceImportData->bDeleteExistingNonCurveCustomAttributes = false;
Options->AnimSequenceImportData->bSetMaterialDriveParameterOnCustomAttribute = false;
Options->AnimSequenceImportData->bRemoveRedundantKeys = false;
Options->AnimSequenceImportData->bDeleteExistingMorphTargetCurves = false;
Options->AnimSequenceImportData->bDoNotImportCurveWithZero = false;
Options->AnimSequenceImportData->bPreserveLocalTransform = false;
// Create the import task
UAssetImportTask* Task = CreateImportTask(SourcePath, DestinationPath, nullptr, Options, bOutSuccess, OutInfoMessage);
if (!bOutSuccess) return nullptr;
// Import the asset
UAnimSequence* RetAsset = Cast<UAnimSequence>(ProcessImportTask(Task, bOutSuccess, OutInfoMessage));
if (!bOutSuccess) return nullptr;
// Return the imported asset
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Import Animation Succeeded - '%s'"), *DestinationPath);
return RetAsset;
}