And attach the modified code that I have modified. Directly loading the Datasmith file requires the help of a plugin to parse the file, which can be difficult to modify. Therefore, I have temporarily changed the plan. I imported the Datasmith file into the engine and packaged it. I used Python to save the properties and hierarchical structure of the model in the scene in a JSON file. At runtime, I read the JSON file to restore the hierarchical structure and properties of the model
.h file
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "IPlatformFilePak.h"
#include "MyPlayerController.generated.h"
/**
*
*/
struct FModelPropertiesData
{
FString Name;
FVector Location;
FRotator Rotation;
FVector Scale;
FString Type;
FString Parent;
FString MeshReference;
TArray<FString> MaterialPaths;
};
UCLASS()
class DBJTEST_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
UFUNCTION(Exec, BlueprintCallable)
void AsyncLoadPak(FString InPakFullPath);
void StartSpawningAssets(TArray<FString> FoundFilenames);
void SpawnNextAssets();
void OnAssetLoaded(FString AssetName);
TSharedPtr<class FPakPlatformFile> PakPlatform;
class IPlatformFile* OldPlatform;
void GetAllFilesInPak(FPakFile& PakFile, const FString& InPath, TArray<FString>& OutFiles);
TArray<FModelPropertiesData> LoadActorDataFromJson(const FString& FilePath);
private:
int32 SpawnIndex;
int32 MeshIndex;
TArray<UStaticMesh*> T_AssetStaticMeshes;
FTimerHandle SpawnTimerHandle;
TArray<FModelPropertiesData> T_ActorData;
TMap<FString, UObject*> TM_MeshData;
FCriticalSection MapMutex;
TMap<FString, UStaticMesh*> TM_FileNames;
TMap<FString, UMaterialInstance*> TM_MaterialNames;
TArray<FString> FileNames;
TArray<UStaticMesh*> AssetStaticMeshes;
FTimerHandle AsyncSpawnTimerHandle;
};
.cpp file
#include "MyPlayerController.h"
#include "GameFramework/Actor.h"
#include "IPlatformFilePak.h"
#include "PlatformFilemanager.h"
#include "Runtime/Engine/Classes/Engine/StaticMeshActor.h"
#include "GenericPlatformFile.h"
#include "Chaos/Core.h"
#include "Engine/AssetManager.h"
#include "Engine/StreamableManager.h"
void AMyPlayerController::BeginPlay()
{
Super::BeginPlay();
OldPlatform = &FPlatformFileManager::Get().GetPlatformFile();
PakPlatform = MakeShareable(new FPakPlatformFile());
PakPlatform->Initialize(&FPlatformFileManager::Get().GetPlatformFile(), TEXT(""));
}
void AMyPlayerController::GetAllFilesInPak(FPakFile& PakFile, const FString& InPath, TArray<FString>& OutFiles)
{
// Retrieve files in the current path
TArray<FString> FoundFileNames;
PakFile.FindPrunedFilesAtPath(FoundFileNames, *InPath, true, false, false);
for (const FString& Filename : FoundFileNames)
{
// Add the current file to the list
OutFiles.Add(Filename);
}
// Retrieve subdirectories under the current path
TArray<FString> Subdirectories;
PakFile.FindPrunedFilesAtPath(Subdirectories, *InPath, false, true, false); //Retrieve directory path, do not retrieve files
// Traverse subdirectories, recursively call GetAllFilesInPak
for (const FString& Subdirectory : Subdirectories)
{
// Recursive call to retrieve all files in the subdirectories
GetAllFilesInPak(PakFile, Subdirectory, OutFiles);
}
}
void AMyPlayerController::AsyncLoadPak(FString InPakFullPath)
{
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, InPakFullPath]()
{
FString JsonFilePath = FPaths::ProjectDir() + TEXT("selected_objects250225.json");
TArray<FModelPropertiesData> ActorData = LoadActorDataFromJson(JsonFilePath); // Retrieve the JSON file of the hierarchical structure properties of scene objects
TArray<UObject*> FoundObjects;
FPlatformFileManager::Get().SetPlatformFile(*PakPlatform.Get());
FString PakFileFullPath = InPakFullPath;
TRefCountPtr<FPakFile> TmpPak = new FPakFile(PakPlatform.Get(), *PakFileFullPath, false);
FString PakMountPoint = TmpPak->GetMountPoint();
int32 Pos = PakMountPoint.Find("Content/");
FString NewMountPoint = PakMountPoint.RightChop(Pos);
NewMountPoint = FPaths::ProjectDir() + NewMountPoint;
// Set mounting point
TmpPak->SetMountPoint(*NewMountPoint);
TArray<FString> FoundFilenames;
GetAllFilesInPak(*TmpPak, *TmpPak->GetMountPoint(), FoundFilenames);
TMap<FString, FString> TemporaryFiles;
TMap<FString, FString> TM_FileNamesTest;
for (const FString& Filename : FoundFilenames)
{
int32 LastPos = INDEX_NONE;
int32 LastPos2 = INDEX_NONE;
bool bFoundSlash = Filename.FindLastChar(TEXT('/'), LastPos);
bool bFoundDot = Filename.FindLastChar(TEXT('.'), LastPos2);
if (bFoundSlash && bFoundDot && Filename.EndsWith(TEXT(".uasset")))
{
TemporaryFiles.Add(Filename.Mid( LastPos + 1, LastPos2 - LastPos - 1), Filename);
}
}
TArray<FString> TempTest;
for (FModelPropertiesData& Data : ActorData)
{
if (TemporaryFiles.Contains(Data.Name))
{
TM_FileNamesTest.Add(Data.Name, TemporaryFiles[Data.Name]);
}
else
{
TM_FileNamesTest.Add(Data.Name, TEXT("None"));
}
}
if (PakPlatform->Mount(*PakFileFullPath, 1, *NewMountPoint))
{
if (FoundFilenames.Num() > 0)
{
for (FString& Filename : FoundFilenames)
{
if (Filename.EndsWith(TEXT(".uasset")))
{
FString NewFileName = Filename;
NewFileName.RemoveFromEnd(TEXT(".uasset"));
int32 Pos = NewFileName.Find("/Content/");
NewFileName = NewFileName.RightChop(Pos + 8);
NewFileName = "/Game" + NewFileName;
// Save the resource path to an array and load it asynchronously in the main thread later
FoundObjects.AddUnique(FSoftObjectPath(NewFileName).ResolveObject());
}
}
T_ActorData = ActorData;
}
}
AsyncTask(ENamedThreads::GameThread, [this, FoundFilenames]()
{
StartSpawningAssets(FoundFilenames);
});
});
}
void AMyPlayerController::StartSpawningAssets(TArray<FString> FoundFilenames)
{
// Use FStreamableManager Asynchronous loading of resources
FStreamableManager& Streamable = UAssetManager::GetStreamableManager();
for (FString& Filename : FoundFilenames)
{
if (Filename.EndsWith(TEXT(".uasset")))
{
FString AssetPath = Filename;
AssetPath.RemoveFromEnd(TEXT(".uasset"));
// replace "/Content/" to "/Game/"
int32 Pos = AssetPath.Find(TEXT("/Content/"));
if (Pos != INDEX_NONE)
{
AssetPath = "/Game" + AssetPath.RightChop(Pos + 8); // 8is"/Content/length
}
// Handle possible double slashes "//"
AssetPath = AssetPath.Replace(TEXT("//"), TEXT("/"));
FSoftObjectPath AssetPathObject(AssetPath);
UE_LOG(LogTemp, Log, TEXT("Requesting async load for: %s"), *AssetPath);
Streamable.RequestAsyncLoad(AssetPathObject, FStreamableDelegate::CreateUObject(this, &AMyPlayerController::OnAssetLoaded, AssetPath));
}
}
}
void AMyPlayerController::OnAssetLoaded(FString AssetPath)
{
UStaticMesh* LoadedMesh = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), nullptr, *AssetPath));
if (!LoadedMesh)
{
UE_LOG(LogTemp, Error, TEXT("Failed to load mesh from: %s"), *AssetPath);
UMaterialInstance* Material = Cast<UMaterialInstance>(StaticLoadObject(UMaterialInstance::StaticClass(), nullptr, *AssetPath));
if (!Material)
{
UE_LOG(LogTemp, Error, TEXT("Failed to load material from: %s"), *AssetPath);
}
MapMutex.Lock();
TM_MaterialNames.Add(AssetPath, Material);
MapMutex.Unlock();
return;
}
UE_LOG(LogTemp, Log, TEXT("Successfully loaded mesh: %s"), *LoadedMesh->GetName());
AssetStaticMeshes.Add(LoadedMesh);
TM_FileNames.Add(LoadedMesh->GetName(), LoadedMesh);
// Set a timer to generate 10 resources every 0.01 seconds
GetWorld()->GetTimerManager().SetTimer(AsyncSpawnTimerHandle, this, &AMyPlayerController::SpawnNextAssets, 0.01f, true);
}
void AMyPlayerController::SpawnNextAssets()
{
int32 NumToSpawn = 10; // Generate 10 at a time
for (int32 i = 0; i < NumToSpawn; i++)
{
if (SpawnIndex >= T_ActorData.Num()) // Generation completed
{
GetWorld()->GetTimerManager().ClearTimer(AsyncSpawnTimerHandle);
UE_LOG(LogTemp, Log, TEXT("All asset spawned!"));
FPlatformFileManager::Get().SetPlatformFile(*OldPlatform);
return;
}
if (T_ActorData[SpawnIndex].Type.Contains(TEXT("Actor")))
{
if (T_ActorData[SpawnIndex].Type.Contains(TEXT("StaticMesh")))
{
AStaticMeshActor* StaticMeshActor = GetWorld()->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), T_ActorData[SpawnIndex].Location, T_ActorData[SpawnIndex].Rotation);
UStaticMesh* SM = TM_FileNames[T_ActorData[SpawnIndex].MeshReference];
if (SM)
{
// StaticMeshActor->SetActorLabel(T_ActorData[SpawnIndex].Name);
// StaticMeshActor->Rename(*T_ActorData[SpawnIndex].Name);
StaticMeshActor->SetActorScale3D(T_ActorData[SpawnIndex].Scale);
StaticMeshActor->SetMobility(EComponentMobility::Movable);
if (T_ActorData[SpawnIndex].MaterialPaths.Num() > 0)
{
UMaterialInstance* Material = TM_MaterialNames[T_ActorData[SpawnIndex].MaterialPaths[0]];
UStaticMeshComponent* SMC = StaticMeshActor->GetStaticMeshComponent();
if (Material)
{
SMC->SetMaterial(0, Material);
}
}
StaticMeshActor->GetStaticMeshComponent()->SetStaticMesh(SM);
}
if (T_ActorData[SpawnIndex].Parent != "None")
{
AActor* ParentActor = Cast<AActor>(TM_MeshData[T_ActorData[SpawnIndex].Parent]);
if (ParentActor && ParentActor->IsValidLowLevel())
{
StaticMeshActor->AttachToActor(ParentActor, FAttachmentTransformRules::KeepRelativeTransform);
StaticMeshActor->SetMobility(EComponentMobility::Static);
}
}
TM_MeshData.Add(T_ActorData[SpawnIndex].Name, StaticMeshActor);
}
else
{
// Dynamically create actors
FActorSpawnParameters SpawnParameters;
AActor* Actor = GetWorld()->SpawnActor<AActor>(AActor::StaticClass(), T_ActorData[SpawnIndex].Location, T_ActorData[SpawnIndex].Rotation, SpawnParameters);
if (Actor)
{
// Add SceneComponent as the root component for ParentActor
USceneComponent* RootComponent = NewObject<USceneComponent>(Actor, TEXT("RootComponent"));
Actor->SetRootComponent(RootComponent);
Actor->SetActorScale3D(T_ActorData[SpawnIndex].Scale);
RootComponent->RegisterComponent();
// Actor->Rename(*T_ActorData[SpawnIndex].Name);
// Actor->SetActorLabel(T_ActorData[SpawnIndex].Name);
if (T_ActorData[SpawnIndex].Parent != "None")
{
AActor* ParentActor = Cast<AActor>(TM_MeshData[T_ActorData[SpawnIndex].Parent]);
if (ParentActor && ParentActor->IsValidLowLevel())
{
Actor->AttachToActor(ParentActor, FAttachmentTransformRules::KeepRelativeTransform);
}
}
TM_MeshData.Add(T_ActorData[SpawnIndex].Name, Actor);
}
}
}
SpawnIndex++;
}
}
TArray<FModelPropertiesData> AMyPlayerController::LoadActorDataFromJson(const FString& FilePath)
{
TArray<FModelPropertiesData> ActorArray;
FString JsonStr;
if (!FFileHelper::LoadFileToString(JsonStr, *FilePath))
{
UE_LOG(LogTemp, Error, TEXT("Failed to load JSON file: %s"), *FilePath);
return ActorArray;
}
TSharedPtr<FJsonValue> JsonParsed;
TSharedRef<TJsonReader<TCHAR>> Reader = TJsonReaderFactory<TCHAR>::Create(JsonStr);
if (!FJsonSerializer::Deserialize(Reader, JsonParsed) || !JsonParsed.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON file"));
return ActorArray;
}
TArray<TSharedPtr<FJsonValue>> JsonArray = JsonParsed->AsArray();
for (TSharedPtr<FJsonValue> Value : JsonArray)
{
TSharedPtr<FJsonObject> JsonObject = Value->AsObject();
if (!JsonObject.IsValid()) continue;
FModelPropertiesData ActorData;
ActorData.Name = JsonObject->GetStringField("name");
TSharedPtr<FJsonObject> Loc = JsonObject->GetObjectField("location");
ActorData.Location = FVector(Loc->GetNumberField("x"), Loc->GetNumberField("y"), Loc->GetNumberField("z"));
TSharedPtr<FJsonObject> Rot = JsonObject->GetObjectField("rotation");
FRotator Rotator;
// The rotation value order in Unreal is different from the Euler angle representation obtained in Python and needs to be swapped (the rotation value of an object can also be obtained and saved using quaternions, which are also used for assigning values in Unreal)
Rotator.Pitch = Rot->GetNumberField("yaw");
Rotator.Roll = Rot->GetNumberField("pitch");
Rotator.Yaw = Rot->GetNumberField("roll");
ActorData.Rotation = Rotator;
TSharedPtr<FJsonObject> Sc = JsonObject->GetObjectField("scale");
ActorData.Scale = FVector(Sc->GetNumberField("x"), Sc->GetNumberField("y"), Sc->GetNumberField("z"));
ActorData.Type = JsonObject->GetStringField("type");
ActorData.Parent = JsonObject->GetStringField("parent");
if (!JsonObject->GetStringField("static_mesh_asset").IsEmpty())
{
ActorData.MeshReference = JsonObject->GetStringField("static_mesh_asset");
}
else
{
ActorData.MeshReference = "None";
}
// Parse the static_mesh_materials array
if (JsonObject->HasField("static_mesh_materials"))
{
TArray<TSharedPtr<FJsonValue>>MaterialsArray = JsonObject->GetArrayField("static_mesh_materials");
for (TSharedPtr<FJsonValue> MaterialsValue : MaterialsArray)
{
FString MaterialPath = MaterialsValue->AsString();
int32 PosIndex = INDEX_NONE;
bool bVaild = MaterialPath.FindLastChar('.', PosIndex);
if (bVaild)
{
MaterialPath = MaterialPath.Mid(0, PosIndex);
}
ActorData.MaterialPaths.Add(MaterialPath);
}
}
ActorArray.Add(ActorData);
}
UE_LOG(LogTemp, Log, TEXT("Successfully parsed %d actors from JSON"), ActorArray.Num());
return ActorArray;
}
type or paste code here