143 lines
5.2 KiB
C++
143 lines
5.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MuCOE/CustomizableObjectInstanceBaker.h"
|
|
|
|
#include "MuCO/CustomizableObject.h"
|
|
#include "MuCO/CustomizableObjectInstance.h"
|
|
#include "MuCO/CustomizableObjectInstancePrivate.h"
|
|
#include "MuCOE/CustomizableObjectInstanceBakingUtils.h"
|
|
|
|
|
|
void UCustomizableObjectInstanceBaker::BakeInstance(UCustomizableObjectInstance* InTargetInstance, const FBakingConfiguration& InBakingConfig, const TSharedPtr<const FOnBakerFinishedWork> InOnBakerFinishedWork)
|
|
{
|
|
// Cache the configuration and the callback used to report the baking results
|
|
Configuration = InBakingConfig;
|
|
|
|
// Cache a delegate that we will later invoke once the baking operation has finished all the work (so we can delete this object safelly)
|
|
if (InOnBakerFinishedWork)
|
|
{
|
|
OnWorkFinished = InOnBakerFinishedWork;
|
|
}
|
|
|
|
if (!InTargetInstance)
|
|
{
|
|
UE_LOG(LogMutable,Error,TEXT("No instance has been provided."))
|
|
FinishBakingOperation(false, nullptr);
|
|
return;
|
|
}
|
|
|
|
// We clone the InTargetInstance to ensure that the baked it is not open in a COI editor.
|
|
InstanceToBake = InTargetInstance->Clone();
|
|
|
|
if (Configuration.OutputPath.IsEmpty())
|
|
{
|
|
UE_LOG(LogMutable,Error,TEXT("No target save path has been provided."))
|
|
FinishBakingOperation(false, nullptr);
|
|
return;
|
|
}
|
|
|
|
// If no name is provided then compose one (allow not having a name)
|
|
// Configuration.OutputFilesBaseName = !Configuration.OutputFilesBaseName.IsEmpty() ? Configuration.OutputFilesBaseName : InstanceToBake.GetName();
|
|
|
|
// Schedule update of the instance for baking
|
|
UpdateInstance();
|
|
}
|
|
|
|
|
|
void UCustomizableObjectInstanceBaker::UpdateInstance()
|
|
{
|
|
// Call the instance update async method
|
|
FInstanceUpdateNativeDelegate UpdateDelegate;
|
|
UpdateDelegate.AddUObject(this, &UCustomizableObjectInstanceBaker::OnInstanceUpdate);
|
|
|
|
// Once the update finishes successfully the baking operation will engage
|
|
ScheduleInstanceUpdateForBaking(*InstanceToBake, UpdateDelegate);
|
|
}
|
|
|
|
|
|
void UCustomizableObjectInstanceBaker::OnInstanceUpdate(const FUpdateContext& Result) const
|
|
{
|
|
const EUpdateResult InstanceUpdateResult = Result.UpdateResult;
|
|
UE_LOG(LogMutable, Display, TEXT("Instance finished update with state : %s."), *UEnum::GetValueAsString(InstanceUpdateResult));
|
|
|
|
if (InstanceToBake->GetPrivate()->SkeletalMeshStatus == ESkeletalMeshStatus::Success)
|
|
{
|
|
Bake();
|
|
}
|
|
else
|
|
{
|
|
// Report a failed baking operation.
|
|
UE_LOG(LogMutable, Display, TEXT("Instance updating for baking failed. Aborting instance baking operation."));
|
|
FinishBakingOperation(false, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
void UCustomizableObjectInstanceBaker::Bake() const
|
|
{
|
|
// Set this flag so we ensure that the systems that make use of it know that should not display interaction messages to the user
|
|
const bool GIsRunningUnattendedScript_Cached = GIsRunningUnattendedScript;
|
|
GIsRunningUnattendedScript = true;
|
|
|
|
// Array with all the packages that did get saved. Packages that failed the saving procedure will not appear in this array.
|
|
TMap<UPackage*, EPackageSaveResolutionType> SavedPackages;
|
|
|
|
// Ensures no interaction from the user will be required (alongside GIsRunningUnattendedScript)
|
|
constexpr bool bIsUnhandledExecution = true;
|
|
|
|
const bool bBakeWasSuccessful = BakeCustomizableObjectInstance(*InstanceToBake, Configuration, bIsUnhandledExecution, SavedPackages);
|
|
|
|
// Generate the array of structs to output based on the packages reported as saved
|
|
TArray<FBakedResourceData> SavedPackagesData;
|
|
{
|
|
// Display a list with the paths of the assets saved
|
|
UE_LOG(LogMutable, Log, TEXT("Saved assets (%d) :"), SavedPackages.Num());
|
|
SavedPackagesData.Reserve(SavedPackages.Num());
|
|
for (const TPair<UPackage*, EPackageSaveResolutionType>& SavedPackagePair : SavedPackages)
|
|
{
|
|
FBakedResourceData BakeData;
|
|
BakeData.AssetPath = SavedPackagePair.Key->GetPathName();
|
|
BakeData.SaveType = SavedPackagePair.Value;
|
|
|
|
// Sanity checks
|
|
check(BakeData.SaveType != EPackageSaveResolutionType::None);
|
|
check(!BakeData.AssetPath.IsEmpty());
|
|
|
|
// Log the path for the caller to know
|
|
UE_LOG(LogMutable, Log, TEXT("\t%s --- %s"), *UEnum::GetValueAsString(BakeData.SaveType), *BakeData.AssetPath);
|
|
|
|
SavedPackagesData.Push(BakeData);
|
|
}
|
|
}
|
|
|
|
// Report that the baking operation has been completed
|
|
UE_LOG(LogMutable, Display, TEXT("Finishing COI baking procedure."));
|
|
FinishBakingOperation(bBakeWasSuccessful, &SavedPackagesData);
|
|
|
|
// Reset the GIsRunningUnattendedScript flag
|
|
GIsRunningUnattendedScript = GIsRunningUnattendedScript_Cached;
|
|
}
|
|
|
|
|
|
void UCustomizableObjectInstanceBaker::FinishBakingOperation(const bool bBakeWasSuccessful, TArray<FBakedResourceData>* InSavedPackagesData) const
|
|
{
|
|
// Report that the baking operation has been completed
|
|
if (Configuration.OnBakeOperationCompletedCallback.IsBound())
|
|
{
|
|
FCustomizableObjectInstanceBakeOutput Output;
|
|
Output.bWasBakeSuccessful = bBakeWasSuccessful;
|
|
if (InSavedPackagesData && !InSavedPackagesData->IsEmpty())
|
|
{
|
|
Output.SavedPackages = MoveTemp(*InSavedPackagesData);
|
|
}
|
|
Configuration.OnBakeOperationCompletedCallback.Execute(Output);
|
|
}
|
|
|
|
// Notify whatever caller that the baker completed it's work and therefore is safe to destroy/discard
|
|
if (OnWorkFinished && OnWorkFinished->IsBound())
|
|
{
|
|
OnWorkFinished->Execute();
|
|
}
|
|
}
|
|
|