// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "MetaHumanCharacterPalette.h" #include "MetaHumanCharacterEditorPipeline.h" #include "MetaHumanPinnedSlotSelection.h" #include "MetaHumanCollection.generated.h" class UMetaHumanCollectionPipeline; class UMetaHumanCollectionEditorPipeline; #if WITH_EDITORONLY_DATA UENUM() enum class EMetaHumanCharacterUnpackPathMode : uint8 { /** * Assets will be unpacked to a subfolder of the Palette's current folder. * The subfolder will have the same name as the Palette. * * For example, when a Palette called A is at path /Game/Palettes, its assets * would be unpacked to the /Game/Palettes/A folder. */ SubfolderNamedForPalette UMETA(Hidden), /** UnpackFolderPath is a relative path from the folder containing the Palette */ Relative, /** UnpackFolderPath is an absolute path */ Absolute }; UENUM() enum class EMetaHumanCharacterAssetsUnpackResult : uint8 { Succeeded, Failed }; DECLARE_DELEGATE_OneParam(FOnMetaHumanCharacterAssetsUnpacked, EMetaHumanCharacterAssetsUnpackResult /* Result */); #endif /** The output of the Character Pipeline's build step for a specific platform */ USTRUCT() struct METAHUMANCHARACTERPALETTE_API FMetaHumanCollectionBuiltData { GENERATED_BODY() public: /** Returns true if this built data has been populated from a successful build */ bool IsValid() const; UPROPERTY() FMetaHumanPaletteBuiltData PaletteBuiltData; UPROPERTY() TArray SortedPinnedSlotSelections; /** The level of quality this data was built for */ UPROPERTY() EMetaHumanCharacterPaletteBuildQuality Quality = EMetaHumanCharacterPaletteBuildQuality::Production; }; /** * A collection of character parts (e.g. MetaHuman Characters, clothing, hairstyles) that target * slots on a Character Pipeline. * * Create a Character Instance from a Collection to assemble a renderable character from the parts * contained in the Collection. */ UCLASS(BlueprintType) class METAHUMANCHARACTERPALETTE_API UMetaHumanCollection : public UMetaHumanCharacterPalette { GENERATED_BODY() public: DECLARE_MULTICAST_DELEGATE_OneParam(FOnPaletteBuilt, EMetaHumanCharacterPaletteBuildQuality /* Quality */); DECLARE_MULTICAST_DELEGATE(FOnPipelineChanged); UMetaHumanCollection(); // Collections should not be created or modified outside the editor #if WITH_EDITOR DECLARE_DELEGATE_OneParam(FOnBuildComplete, EMetaHumanBuildStatus /* Status */); /** Builds the collection so that Character Instances can assemble characters from it */ void Build( const FInstancedStruct& BuildInput, EMetaHumanCharacterPaletteBuildQuality Quality, ITargetPlatform* TargetPlatform, const FOnBuildComplete& OnComplete, const TArray& PinnedSlotSelections = TArray(), const TArray& ItemsToExclude = TArray()); /** * Moves any built assets stored within this Collection to their own asset packages, making them * standalone assets that can be referenced from other objects. * * The Collection can still be used as normal after this, as it will still reference the unpacked * assets as it did before they were unpacked. */ void UnpackAssets(const FOnMetaHumanCharacterAssetsUnpacked& OnComplete = FOnMetaHumanCharacterAssetsUnpacked()); /** * Sets the default Pipeline from the project settings. * * Call this after constructing a Collection if you don't have a specific Pipeline to use. */ void SetDefaultPipeline(); /** * Set the Pipeline for this Collection to use. * * It's not necessary to provide each Collection instance with a unique Pipeline instance, as the * Pipeline is intended to be stateless. However, the Pipeline instance will be editable in * Details panels wherever the Collection is visible for editing, and users editing a Pipeline's * properties from there may be surprised if these edits affect other Collections. */ void SetPipeline(TNotNull InPipeline); /** * Sets the Pipeline to be an instance of the given class */ void SetPipelineFromClass(TSubclassOf InPipelineClass); /** Convenience function to access the editor pipeline */ const UMetaHumanCollectionEditorPipeline* GetEditorPipeline() const; virtual const UMetaHumanCharacterEditorPipeline* GetPaletteEditorPipeline() const override; #endif /** * The Pipeline targeted by this Collection. * * May be null if the user hasn't set a pipeline yet. */ UMetaHumanCollectionPipeline* GetMutablePipeline(); /** * The Pipeline targeted by this Collection. * * May be null if the user hasn't set a pipeline yet. */ const UMetaHumanCollectionPipeline* GetPipeline() const; virtual const UMetaHumanCharacterPipeline* GetPalettePipeline() const override; /** Note that the returned data is not guaranteed to be valid. Call IsValid on the result to check. */ const FMetaHumanCollectionBuiltData& GetBuiltData(EMetaHumanCharacterPaletteBuildQuality Quality) const; /** * The Collection contains a default instance that is used for preview. * * The default instance is a valid instance in its own right and can be used at runtime. * * Guaranteed to be non-null. */ TNotNull GetMutableDefaultInstance(); TNotNull GetDefaultInstance() const; #if WITH_EDITORONLY_DATA void SetBuiltData(EMetaHumanCharacterPaletteBuildQuality Quality, FMetaHumanCollectionBuiltData&& Data); #endif // WITH_EDITORONLY_DATA #if WITH_EDITOR // Begin UObject interface virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; // End UObject interface #endif // WITH_EDITOR /** * Resolves virtual slots in the given array of selections. * * For each selection in the array, if that selection targets a virtual slot, it will be * updated to target the underlying real slot. * * Once this is done, code using the resulting array can just operate on real slots and doesn't * have to handle virtual slots at all. * * This function assumes that PipelineSpec is valid (i.e. PipelineSpec->IsValid() returns true), * otherwise it may assert or never return. * * Any selections that don't resolve to valid items in the Collection will be omitted from the * returned array. */ [[nodiscard]] TArray PropagateVirtualSlotSelections(const TArray& Selections) const; #if WITH_EDITOR /** * Delegate fired when a new Pipeline has been set on this Collection. * * This can only happen in editor. * * If any changes need to be made to the Collection in response to the Pipeline changing, such as * removing items from slots that don't exist on the new Pipeline, those changes will be made * before this delegate is fired. */ FOnPipelineChanged OnPipelineChanged; #endif // WITH_EDITOR #if WITH_EDITORONLY_DATA /** The mode for determining which folder to unpack the Collection's assets to */ UPROPERTY(EditAnywhere, Category = "Targets", DisplayName = "Root Relative", meta = (Tooltip = "The mode for determining whether the Root path is relative to the character asset path")) EMetaHumanCharacterUnpackPathMode UnpackPathMode = EMetaHumanCharacterUnpackPathMode::Relative; /** The folder path that assets will be unpacked to. Interpreted according to UnpackPathMode. */ UPROPERTY(EditAnywhere, Category = "Targets", DisplayName = "Root Path", meta = (EditCondition = "UnpackPathMode != EMetaHumanCharacterUnpackPathMode::SubfolderNamedForPalette", EditConditionHides, Tooltip = "Project directory to place the generated assets")) FString UnpackFolderPath; /** Returns the folder path where the assets will be unpacked, depending on the UnpackPathMode */ FString GetUnpackFolder() const; #endif // WITH_EDITORONLY_DATA /** Delegate fired when the Collection has finished building, if it succeeded */ FOnPaletteBuilt OnPaletteBuilt; private: /** The MetaHuman Collection Pipeline used to build this collection */ UPROPERTY(EditAnywhere, Instanced, Category = "Character") TObjectPtr Pipeline; UPROPERTY() TObjectPtr DefaultInstance; UPROPERTY() FMetaHumanCollectionBuiltData ProductionBuiltData; #if WITH_EDITORONLY_DATA /** * Built data from Preview quality builds. * * Should be recreated as needed for preview. Shouldn't be saved or copied. */ UPROPERTY(Transient, DuplicateTransient) FMetaHumanCollectionBuiltData PreviewBuiltData; /** * A per-item cache that persists between builds. * * There is not guaranteed to be an entry for every item. */ UPROPERTY() TMap ItemBuildCache; UPROPERTY() FMetaHumanPaletteBuildCacheEntry PaletteBuildCache; #endif // WITH_EDITORONLY_DATA /** True if the assets in this Collection have been unpacked and are in their own packages. */ UPROPERTY() bool bIsUnpacked; };