Files
2025-05-18 13:04:45 +08:00

256 lines
9.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InterchangeFbxParser.h"
#include "CoreMinimal.h"
#include "FbxAPI.h"
#include "HAL/CriticalSection.h"
#include "InterchangeTextureNode.h"
#include "Misc/ScopeLock.h"
#include "Nodes/InterchangeBaseNodeContainer.h"
#include "Serialization/MemoryWriter.h"
#include "UObject/StrongObjectPtr.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Package.h"
#include "InterchangeHelper.h"
#include "InterchangeCommonAnimationPayload.h"
#define LOCTEXT_NAMESPACE "InterchangeFbxParser"
namespace UE::Interchange
{
FInterchangeFbxParser::FInterchangeFbxParser()
{
ResultsContainer.Reset(NewObject<UInterchangeResultsContainer>(GetTransientPackage()));
FbxParserPrivate = MakeUnique<Private::FFbxParser>(ResultsContainer.Get());
}
FInterchangeFbxParser::~FInterchangeFbxParser()
{
ReleaseResources();
}
void FInterchangeFbxParser::ReleaseResources()
{
ResultsContainer = nullptr;
FbxParserPrivate = nullptr;
}
void FInterchangeFbxParser::Reset()
{
ResultPayloads.Reset();
FbxParserPrivate->Reset();
}
void FInterchangeFbxParser::SetResultContainer(UInterchangeResultsContainer* Result)
{
InternalResultsContainer = Result;
FbxParserPrivate->SetResultContainer(Result);
}
void FInterchangeFbxParser::SetConvertSettings(const bool InbConvertScene, const bool InbForceFrontXAxis, const bool InbConvertSceneUnit, const bool InbKeepFbxNamespace)
{
FbxParserPrivate->SetConvertSettings(InbConvertScene, InbForceFrontXAxis, InbConvertSceneUnit, InbKeepFbxNamespace);
}
void FInterchangeFbxParser::LoadFbxFile(const FString& Filename, const FString& ResultFolder)
{
check(FbxParserPrivate.IsValid());
SourceFilename = Filename;
ResultsContainer->Empty();
//Since we are not in main thread we cannot use TStrongPtr, so we will add the object to the root and remove it when we are done
UInterchangeBaseNodeContainer* Container = NewObject<UInterchangeBaseNodeContainer>(GetTransientPackage(), NAME_None);
if (!ensure(Container != nullptr))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantAllocate", "Cannot allocate base node container to add FBX scene data.");
}
return;
}
if (!FbxParserPrivate->LoadFbxFile(Filename, *Container))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantLoadFbxFile", "Cannot load the FBX file.");
}
return;
}
ResultFilepath = ResultFolder + TEXT("/SceneDescription.itc");
Container->AddToRoot();
FbxParserPrivate->FillContainerWithFbxScene(*Container);
Container->SaveToFile(ResultFilepath);
Container->RemoveFromRoot();
}
void FInterchangeFbxParser::LoadFbxFile(const FString& Filename, UInterchangeBaseNodeContainer& BaseNodecontainer)
{
SourceFilename = Filename;
if (!ensure(FbxParserPrivate.IsValid()))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantLoadFbxFile_ParserInvalid", "FInterchangeFbxParser::LoadFbxFile: Cannot load the FBX file. The internal fbx parser is invalid.");
}
return;
}
if (!FbxParserPrivate->LoadFbxFile(Filename, BaseNodecontainer))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantLoadFbxFile_ParserError", "FInterchangeFbxParser::LoadFbxFile: Cannot load the FBX file. There was an error when parsing the file.");
}
return;
}
FbxParserPrivate->FillContainerWithFbxScene(BaseNodecontainer);
}
void FInterchangeFbxParser::FetchPayload(const FString& PayloadKey, const FString& ResultFolder)
{
ResultsContainer->Empty();
if (!ensure(FbxParserPrivate.IsValid()))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = PayloadKey;
Error->Text = LOCTEXT("CantFetchPayload_ParserInvalid", "FInterchangeFbxParser::FetchPayload: Cannot fetch the payload. The internal fbx parser is invalid.");
}
return;
}
FString PayloadFilepathCopy;
{
FScopeLock Lock(&ResultPayloadsCriticalSection);
FString& PayloadFilepath = ResultPayloads.FindOrAdd(PayloadKey);
//To avoid file path with too many character, we hash the payloadKey so we have a deterministic length for the file path.
FString PayloadKeyHash = Private::HashString(PayloadKey);
PayloadFilepath = ResultFolder + TEXT("/") + PayloadKeyHash + FString::FromInt(UniqueIdCounter.IncrementExchange()) + TEXT(".payload");
//Copy the map filename key because we are multithreaded and the TMap can be reallocated
PayloadFilepathCopy = PayloadFilepath;
}
if (!FbxParserPrivate->FetchPayloadData(PayloadKey, PayloadFilepathCopy))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantFetchPayload", "Cannot fetch FBX payload data.");
}
return;
}
}
FString FInterchangeFbxParser::FetchMeshPayload(const FString& PayloadKey, const FTransform& MeshGlobalTransform, const FString& ResultFolder)
{
ResultsContainer->Empty();
FString PayloadFilepathCopy;
FString ResultPayloadUniqueId = PayloadKey + MeshGlobalTransform.ToString();
if (!ensure(FbxParserPrivate.IsValid()))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = PayloadKey;
Error->Text = LOCTEXT("CantFetchMeshPayload_ParserInvalid", "FInterchangeFbxParser::FetchMeshPayload: Cannot fetch the mesh payload. The internal fbx parser is invalid.");
}
return ResultPayloadUniqueId;
}
//If we already have extract this mesh, no need to extract again
if (ResultPayloads.Contains(ResultPayloadUniqueId))
{
return ResultPayloadUniqueId;
}
{
FScopeLock Lock(&ResultPayloadsCriticalSection);
FString& PayloadFilepath = ResultPayloads.FindOrAdd(ResultPayloadUniqueId);
//To avoid file path with too many character, we hash the payloadKey so we have a deterministic length for the file path.
FString PayloadKeyHash = Private::HashString(PayloadKey);
PayloadFilepath = ResultFolder + TEXT("/") + PayloadKeyHash + FString::FromInt(UniqueIdCounter.IncrementExchange()) + TEXT(".payload");
//Copy the map filename key because we are multithreaded and the TMap can be reallocated
PayloadFilepathCopy = PayloadFilepath;
}
if (!FbxParserPrivate->FetchMeshPayloadData(PayloadKey, MeshGlobalTransform, PayloadFilepathCopy))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantFetchPayload", "Cannot fetch FBX payload data.");
}
}
return ResultPayloadUniqueId;
}
#if WITH_ENGINE
void FInterchangeFbxParser::FetchMeshPayload(const FString& PayloadKey, const FTransform& MeshGlobalTransform, FMeshPayloadData& OutMeshPayloadData)
{
if (!FbxParserPrivate->FetchMeshPayloadData(PayloadKey, MeshGlobalTransform, OutMeshPayloadData))
{
if (UInterchangeResultError_Generic* Error = AddMessage<UInterchangeResultError_Generic>())
{
Error->SourceAssetName = SourceFilename;
Error->Text = LOCTEXT("CantFetchPayload", "Cannot fetch FBX payload data.");
}
}
}
#endif
TArray<FString> FInterchangeFbxParser::GetJsonLoadMessages() const
{
TArray<FString> JsonResults;
for (UInterchangeResult* Result : GetResultContainer()->GetResults())
{
JsonResults.Add(Result->ToJson());
}
return JsonResults;
}
UInterchangeResultsContainer* FInterchangeFbxParser::GetResultContainer() const
{
if (InternalResultsContainer)
{
return InternalResultsContainer;
}
ensure(ResultsContainer);
return ResultsContainer.Get();
}
void FInterchangeFbxParser::FetchAnimationBakeTransformPayloads(const TArray<UE::Interchange::FAnimationPayloadQuery>& PayloadQueries, const FString& ResultFolder)
{
FbxParserPrivate->FetchAnimationBakeTransformPayload(PayloadQueries, ResultFolder, &ResultPayloadsCriticalSection, UniqueIdCounter, ResultPayloads);
}
//Used by DispatchWorker
TMap<FString, FString> FInterchangeFbxParser::FetchAnimationBakeTransformPayloads(const FString& PayloadQueriesJsonString, const FString& ResultFolder)
{
TMap<FString, FString> HashToFilePaths;
TArray<UE::Interchange::FAnimationPayloadQuery> PayloadQueries;
UE::Interchange::FAnimationPayloadQuery::FromJson(PayloadQueriesJsonString, PayloadQueries);
FbxParserPrivate->FetchAnimationBakeTransformPayload(PayloadQueries, ResultFolder, &ResultPayloadsCriticalSection, UniqueIdCounter, ResultPayloads);
//Acquire FilePaths:
for (const UE::Interchange::FAnimationPayloadQuery& PayloadQuery : PayloadQueries)
{
HashToFilePaths.Add(PayloadQuery.GetHashString(), GetResultPayloadFilepath(PayloadQuery.GetHashString()));
}
return HashToFilePaths;
}
}//ns UE::Interchange
#undef LOCTEXT_NAMESPACE