Importing Assets with C++ to Unreal Engine
Importing assets using UBlueprintFunctionLibrary
class works in the editor mode only. While we use it in the editor, we need to extend project's build settings through .Build.cs
file for 2 additional modules, see below.
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"AssetTools",
"UnrealEd",
}
);
};
Usually we will use 2 following custom help functions for importing - CreateImportTask
and ProcessImportTask
. They may be defined within the same UExampleClass
or in a separated class we will ink to.
-
CreateImportTask
custom help function.h/** * Editor Only - Will not work in packaged build. * * Create an import task that you can then process to import any kind of assets * * @param SourcePath The path of the source file: "C:/Temp/MyTexture.png" * @param DestinationPath The path of the imported asset: "/Game/Folder/MyTexture" * @param ExtraFactory Optional. Some asset types require a special factory to import them * @param ExtraOptions Optional. Some asset types have some extra settings you can set * @param bOutSuccess If the action was a success or not * @param OutInfoMsg More information about the action's result * * @return The import task */ UFUNCTION(BlueprintCallable, Category = "Import Basic Assets") static class UAssetImportTask* CreateImportTask(FString SourcePath, FString DestinationPath, UFactory* ExtraFactory, UObject* ExtraOptions, bool& bOutSuccess, FString& OutInfoMsg);
.cpp#include "Editor/UnrealEd/Public/AssetImportTask.h" // UnrealEd (Editor Only) #include "AssetToolsModule.h" // AssetTools (Editor Only) UAssetImportTask* UExampleClass::CreateImportTask(FString SourcePath, FString DestinationPath, UFactory* ExtraFactory, UObject* ExtraOptions, bool& bOutSuccess, FString& OutInfoMsg) { // Create task object UAssetImportTask* Task = NewObject<UAssetImportTask>(); if (Task == nullptr) { bOutSuccess = false; OutInfoMsg = FString::Printf(TEXT("Create Import Task Failed - Was not able to create the import task object. That shouldn't really happen. - '%s'"), *SourcePath); return nullptr; } // Set path informations - fill with function parameters Task->Filename = SourcePath; Task->DestinationPath = FPaths::GetPath(DestinationPath); Task->DestinationName = FPaths::GetCleanFilename(DestinationPath); // Set basic options Task->bSave = false; // save file once task completed? Task->bAutomated = true; // automated = no popups for the user Task->bAsync = false; Task->bReplaceExisting = true; Task->bReplaceExistingSettings = false; // Optional extra factory and options Task->Factory = ExtraFactory; Task->Options = ExtraOptions; // Return the task bOutSuccess = true; OutInfoMsg = FString::Printf(TEXT("Create Import Task Succeeded - '%s'"), *SourcePath); return Task; }
.Build.csPrivateDependencyModuleNames.AddRange ( new string[] { // Default Modules "Core", "CoreUObject", "Engine", // New Modules - Editor Only "AssetTools",, "UnrealEd", } );
In the configuration above, imported assets are not saved automatically due to
Task->bSave = false
setup. -
ProcessImportTask
custom help function.h/** * Editor Only - Will not work in packaged build. * * Process the import task to import the assets * * @param ImportTask The task you want to process * @param bOutSuccess If the action was a success or not * @param OutInfoMsg More information about the action's result * * @return The imported asset */ UFUNCTION(BlueprintCallable, Category = "Import Basic Assets") static UObject* ProcessImportTask(UAssetImportTask* ImportTask, bool& bOutSuccess, FString& OutInfoMsg);
.cppUObject* UExampleClass::ProcessImportTask(UAssetImportTask* ImportTask, bool& bOutSuccess, FString& OutInfoMsg) { if (ImportTask == nullptr) { bOutSuccess = false; OutInfoMsg = FString::Printf(TEXT("Process Import Task Failed - The task was invalid. Cannot process.")); return nullptr; } // Get the AssetTools module FAssetToolsModule* AssetTools = FModuleManager::LoadModulePtr<FAssetToolsModule>("AssetTools"); if (AssetTools == nullptr) { bOutSuccess = false; OutInfoMsg = FString::Printf(TEXT("Process Import Task Failed - The AssetTools module is invalid. - '%s'"), *ImportTask->Filename); return nullptr; } // Import the asset AssetTools->Get().ImportAssetTasks({ Task }); // Check if anything was imported during the process if (ImportTask->GetObjects().Num() == 0) { bOutSuccess = false; OutInfoMsg = FString::Printf(TEXT("Process Import Task Failed - Nothing was imported. Is your file valid? Is the asset type supported? - '%s'"), *ImportTask->Filename); return nullptr; } // Because some import tasks actually create multiple assets (e.g. SkeletalMesh), we manually get the right asset UObject* ImportedAsset = StaticLoadObject(UObject::StaticClass(), nullptr, *FPaths::Combine(ImportTask->DestinationPath, ImportTask->DestinationName)); // Return the asset bOutSuccess = true; OutInfoMsg = FString::Printf(TEXT("Process Import Task Succeeded - '%s'"), *ImportTask->Filename); return ImportedAsset; }
.Build.csPrivateDependencyModuleNames.AddRange ( new string[] { // Default Modules "Core", "CoreUObject", "Engine", // New Modules - Editor Only "AssetTools",, "UnrealEd", } );
Import of basic data types (textures, sounds...) (Editor based
)
As mentioned, we work in an inherited class from UBlueprintFunctionLibrary
, thus our header file should look like below:
UCLASS()
class UExampleClass : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/**
* Editor Only - Will not work in packaged build.
*
* Create and process a basic import task.
* Will work for the basic asset types like textures and sounds.
* Can also be used for meshes, but you might not have all the control you want.
*
* @param SourcePath The path of the source file: "C:/Temp/MyTexture.png"
* @param DestinationPath The path of the imported asset: "/Game/Folder/MyTexture"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The imported asset
*/
UFUNCTION(BlueprintCallable, Category = "Import Basic Assets")
static UObject* ImportAsset(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg);
// CreateImportTask help function
// ProcessImportTask help function
};
#include "Editor/UnrealEd/Public/AssetImportTask.h" // UnrealEd (Editor Only)
#include "AssetToolsModule.h" // AssetTools (Editor Only)
UObject* UExampleClass::ImportAsset(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg)
{
// Create the import task
UAssetImportTask* Task = CreateImportTask(SourcePath, DestinationPath, nullptr, nullptr, bOutSuccess, OutInfoMsg);
if (!bOutSuccess) return nullptr;
// Import the asset
UObject* RetAsset = ProcessImportTask(Task, bOutSuccess, OutInfoMsg);
if (!bOutSuccess) return nullptr;
// Return the imported asset
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Import Asset Succeeded - '%s'"), *DestinationPath);
return RetAsset;
}
In the similar way, there's possible:
Reimport Asset (Editor based
)
The code below is a C++ way of following manual operation: "Selecting an asset in Content
window" → Right mouse click → Selecting Reimport
option or alternatively Reimport With New File
option.
/**
* Editor Only - Will not work in packaged build.
*
* Reimport an already imported asset
*
* @param AssetPath The path of the asset to reimport: "/Game/Folder/MyTexture"
* @param SourcePathOverride (Optional) The path of the source file (if you want to use a new file): "C:/Temp/MyTexture.png"
* @param OutInfoMsg More information about the action's result
* @param return If the action was a success or not
*/
UFUNCTION(BlueprintCallable, Category = "Reimport Asset")
static bool ReimportAsset(FString AssetPath, FString SourcePathOverride, FString& OutInfoMsg);
#include "EditorReimportHandler.h" // UnrealEd
bool UExampleClass::ReimportAsset(FString AssetPath, FString SourcePathOverride, FString& OutInfoMsg)
{
// Load existing asset
UObject* ExistingAsset = StaticLoadObject(UObject::StaticClass(), nullptr, *AssetPath);
if (ExistingAsset == nullptr)
{
OutInfoMsg = FString::Printf(TEXT("Reimport Asset Failed - Asset isn't valid. '%s'"), *AssetPath);
return false;
}
// Update path
if (SourcePathOverride != "" && !UExampleClass::SetAssetImportPath(AssetPath, SourcePathOverride, OutInfoMsg)) return false;
// Reimport
if (FReimportManager::Instance()->Reimport(ExistingAsset))
{
OutInfoMsg = FString::Printf(TEXT("Reimport Asset Succeeded - '%s'"), *AssetPath);
return true;
}
bool bOutSuccess;
FString Path = UExampleClass::GetAssetImportPath(AssetPath, bOutSuccess, OutInfoMsg);
OutInfoMsg = FString::Printf(TEXT("Reimport Asset Failed - Is the source path valid? \n'%s'"), *Path);
return False;
}
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// New Modules - Editor Only
"UnrealEd",
}
);
Get / Set Asset Import Source Path (Editor based
)
The import path (File Path
) is the path of the file that was used to import the asset to Unreal Engine. The code below allows to get File Path of any asset as well as change it to link at another file.
Get Asset Import Path
/**
* Editor Only - Will not work in packaged build.
*
* Get the path used to import his asset
*
* @param AssetPath The path of the asset: "/Game/Folder/MyTexture"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The path used to import this asset
*/
UFUNCTION(BlueprintCallable, Category = "Modify Asset Import Path")
static FString GetAssetImportPath(FString AssetPath, bool& bOutSuccess, FString& OutInfoMsg);
#include "EditorReimportHandler.h" // UnrealEd
#include "EditorFramework/AssetImportData.h" // Engine
FString UExampleClass::GetAssetImportPath(FString AssetPath, bool& bOutSuccess, FString& OutInfoMsg)
{
// Load asset
UObject* Asset = StaticLoadObject(UObject::StaticClass(), nullptr, *AssetPath);
if (Asset == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Get Asset Import Path Failed - Asset isn't valid. '%s'"), *AssetPath);
return "";
}
// Can we use the ReimportManager to get the import path?
TArray<FString> Paths = TArray<FString>();
if (FReimportManager::Instance()->CanReimport(Asset, &Paths) && Paths.Num() > 0)
{
FString Path = Paths[0];
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Get Asset Import Path Succeeded [ReimportManager] - '%s'\n'%s'"), *AssetPath, *Path);
return Path;
}
else
{
// Get the path manually
UAssetImportData* AssetImportData = GetAssetImportData(Asset);
if (AssetImportData != nullptr)
{
FString Path = AssetImportData->GetFirstFilename();
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Get Asset Import Path Succeeded - '%s'\n'%s'"), *AssetPath, *Path);
return Path;
}
}
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Get Asset Import Path Failed - Asset class not in GetAssetImportData: '%s'"), *AssetPath);
return "";
}
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// New Modules - Editor Only
"UnrealEd",
}
);
Set Asset Import Path
/**
* Editor Only - Will not work in packaged build.
*
* Set the path used to import his asset
*
* @param AssetPath The path of the asset: "/Game/Folder/MyTexture"
* @param NewImportPath The path of the new source files: "C:/Temp/MyTexture.png"
* @param OutInfoMsg More information about the action's result
* @param return If the action was a success or not
*/
UFUNCTION(BlueprintCallable, Category = "Modify Asset Import Path")
static bool SetAssetImportPath(FString AssetPath, FString NewImportPath, FString& OutInfoMsg);
#include "EditorReimportHandler.h" // UnrealEd
#include "EditorFramework/AssetImportData.h" // Engine
bool UExampleClass::SetAssetImportPath(FString AssetPath, FString NewImportPath, FString& OutInfoMsg)
{
// Load asset
UObject* Asset = StaticLoadObject(UObject::StaticClass(), nullptr, *AssetPath);
if (Asset == nullptr)
{
OutInfoMsg = FString::Printf(TEXT("Set Asset Import Path Failed - Asset isn't valid. '%s'"), *AssetPath);
return false;
}
// Can we use the ReimportManager to set the import path?
if (FReimportManager::Instance()->CanReimport(Asset, nullptr))
{
FReimportManager::Instance()->UpdateReimportPath(Asset, NewImportPath, 0);
OutInfoMsg = FString::Printf(TEXT("Set Asset Import Path Succeeded [ReimportManager] - '%s'\n'%s'"), *AssetPath, *NewImportPath);
return true;
} else {
// Set the path manually
UAssetImportData* AssetImportData = GetAssetImportData(Asset);
if (AssetImportData != nullptr)
{
AssetImportData->SetSourceFiles({ NewImportPath });
OutInfoMsg = FString::Printf(TEXT("Set Asset Import Path Succeeded - '%s'\n'%s'"), *AssetPath, *NewImportPath);
return true;
}
}
OutInfoMsg = FString::Printf(TEXT("Set Asset Import Path Failed - Asset class not in GetAssetImportData: '%s'"), *AssetPath);
return false;
}
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// New Modules - Editor Only
"UnrealEd",
}
);
- Setting
Source File
path forMeshes
works only when setting a path with unreal-supported asset type endings (.fbx, and so on). - Setting
Source File
forTexture
does not care about extension of the source path - Setting
Source File
forSound
does not care about extension of the source path
Get the Asset Import Data based on the asset class
/**
* Editor Only - Will not work in packaged build.
*
* Get the Asset Import Data based on the asset class
*
* @param Asset The asset
*
* @return Asset import data
*/
UFUNCTION(BlueprintCallable, Category = "Modify Asset Import Path")
static class UAssetImportData* GetAssetImportData(UObject* Asset);
#include "Engine/StaticMesh.h" // Engine
#include "Engine/SkeletalMesh.h" // Engine
#include "Engine/Texture.h" // Engine
#include "Animation/AnimSequence.h" // Engine
#include "Engine/DataTable.h" // Engine
#include "Sound/SoundWave.h" // Engine
UAssetImportData* UExampleClass::GetAssetImportData(UObject* Asset)
{
if (Cast<UStaticMesh>(Asset) != nullptr) // UStaticMesh
{
return Cast<UStaticMesh>(Asset)->AssetImportData;
}
else if (Cast<USkeletalMesh>(Asset) != nullptr) // USkeletalMesh
{
return Cast<USkeletalMesh>(Asset)->GetAssetImportData();
}
else if (Cast<UTexture>(Asset) != nullptr) // UTexture
{
return Cast<UTexture>(Asset)->AssetImportData;
}
else if (Cast<UAnimSequence>(Asset) != nullptr) // UAnimSequence
{
return Cast<UAnimSequence>(Asset)->AssetImportData;
}
else if (Cast<UDataTable>(Asset) != nullptr) // UDataTable
{
return Cast<UDataTable>(Asset)->AssetImportData;
}
else if (Cast<USoundWave>(Asset) != nullptr) // USoundWave
{
return Cast<USoundWave>(Asset)->AssetImportData;
}
// Add all other asset types you want to support.
return nullptr;
}
Import Alembic Geometry Cache
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
/**
* Editor Only - Will not work in packaged build.
*
* Create and process a GeometryCache import task.
*
* @param SourcePath The path of the source file: "C:/Temp/MyGeometryCache.abc"
* @param DestinationPath The path of the imported asset: "/Game/Folder/MyGeometryCache"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The imported GeometryCache
*/
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Import Alembic Geometry Cache")
static UGeometryCache* ImportGeometryCache(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg);
#include "GeometryCache.h" // GeometryCache
#include "AlembicImportFactory.h" // AlembicImporter (Editor Only)
#include "AbcImportSettings.h" // AlembicLibrary
UGeometryCache* UExampleClass::ImportGeometryCache(FString SourcePath, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg)
{
// Create factory to import a Geometry Cache
UAlembicImportFactory* AbcFactory = NewObject<UAlembicImportFactory>();
// Alembic
AbcFactory->ImportSettings->ImportType = EAlembicImportType::GeometryCache;
// Geometry Cache - Only useful when ImportType is EAlembicImportType::GeometryCache
AbcFactory->ImportSettings->GeometryCacheSettings.bFlattenTracks = false;
AbcFactory->ImportSettings->GeometryCacheSettings.bStoreImportedVertexNumbers = false;
AbcFactory->ImportSettings->GeometryCacheSettings.bApplyConstantTopologyOptimizations = false;
AbcFactory->ImportSettings->GeometryCacheSettings.bCalculateMotionVectorsDuringImport_DEPRECATED = false;
AbcFactory->ImportSettings->GeometryCacheSettings.MotionVectors = EAbcGeometryCacheMotionVectorsImport::NoMotionVectors;
AbcFactory->ImportSettings->GeometryCacheSettings.bOptimizeIndexBuffers = false;
AbcFactory->ImportSettings->GeometryCacheSettings.CompressedPositionPrecision = 0.01f;
AbcFactory->ImportSettings->GeometryCacheSettings.CompressedTextureCoordinatesNumberOfBits = 10;
// Static Mesh - Only useful when ImportType is EAlembicImportType::StatisMesh
AbcFactory->ImportSettings->StaticMeshSettings.bMergeMeshes = true;
AbcFactory->ImportSettings->StaticMeshSettings.bPropagateMatrixTransformations = true;
AbcFactory->ImportSettings->StaticMeshSettings.bGenerateLightmapUVs = true;
// Compression - Only useful when ImportType is EAlembicImportType::Skeletal
AbcFactory->ImportSettings->CompressionSettings.bMergeMeshes = false;
AbcFactory->ImportSettings->CompressionSettings.bBakeMatrixAnimation = true;
AbcFactory->ImportSettings->CompressionSettings.BaseCalculationType = EBaseCalculationType::PercentageBased;
AbcFactory->ImportSettings->CompressionSettings.PercentageOfTotalBases = 100.0f;
AbcFactory->ImportSettings->CompressionSettings.MaxNumberOfBases = 0;
AbcFactory->ImportSettings->CompressionSettings.MinimumNumberOfVertexInfluencePercentage = 0.0f;
// Sampling
AbcFactory->ImportSettings->SamplingSettings.SamplingType = EAlembicSamplingType::PerFrame;
AbcFactory->ImportSettings->SamplingSettings.FrameSteps = 1;
AbcFactory->ImportSettings->SamplingSettings.TimeSteps = 0.0f;
AbcFactory->ImportSettings->SamplingSettings.FrameStart = 0;
AbcFactory->ImportSettings->SamplingSettings.FrameEnd = 29;
AbcFactory->ImportSettings->SamplingSettings.bSkipEmpty = false;
// Normal Calculation
AbcFactory->ImportSettings->NormalGenerationSettings.bRecomputeNormals = false;
AbcFactory->ImportSettings->NormalGenerationSettings.HardEdgeAngleThreshold = 0.9f;
AbcFactory->ImportSettings->NormalGenerationSettings.bForceOneSmoothingGroupPerObject = false;
AbcFactory->ImportSettings->NormalGenerationSettings.bIgnoreDegenerateTriangles = true;
AbcFactory->ImportSettings->NormalGenerationSettings.bSkipComputingTangents = false;
// Materials
AbcFactory->ImportSettings->MaterialSettings.bCreateMaterials = false;
AbcFactory->ImportSettings->MaterialSettings.bFindMaterials = false;
// Conversion
AbcFactory->ImportSettings->ConversionSettings.Preset = EAbcConversionPreset::Custom;
AbcFactory->ImportSettings->ConversionSettings.bFlipU = false;
AbcFactory->ImportSettings->ConversionSettings.bFlipV = true;
AbcFactory->ImportSettings->ConversionSettings.Scale = FVector(1000.0f);
AbcFactory->ImportSettings->ConversionSettings.Rotation = FVector::ZeroVector;
// Create the import task
UAssetImportTask* Task = UExampleClass::CreateImportTask(SourcePath, DestinationPath, AbcFactory, nullptr, bOutSuccess, OutInfoMsg);
if (!bOutSuccess) return nullptr;
// Import the asset
UGeometryCache* RetAsset = Cast<UGeometryCache>(UExampleClass::ProcessImportTask(Task, bOutSuccess, OutInfoMsg));
if (!bOutSuccess) return nullptr;
// Return the imported asset
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Import Geometry Cache Succeeded - '%s'"), *DestinationPath);
return RetAsset;
}
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// Required Modules
"GeometryCache",
"AlembicLibrary",
}
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"AssetTools",
"AlembicImporter",
}
);
}
);