Files
UnrealEngine/Engine/Plugins/Animation/RigLogic/Source/RigLogicModule/Public/DNAAsset.h
2025-05-18 13:04:45 +08:00

165 lines
5.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "UObject/ObjectMacros.h"
#include "Engine/AssetUserData.h"
#include "Interfaces/Interface_AssetUserData.h"
#include "RigLogic.h"
#include "DNAAsset.generated.h"
#define UE_API RIGLOGICMODULE_API
DECLARE_LOG_CATEGORY_EXTERN(LogDNAAsset, Log, All);
class IDNAReader;
class IBehaviorReader;
class IGeometryReader;
class FRigLogicMemoryStream;
class UAssetUserData;
class USkeleton;
class USkeletalMesh;
class USkeletalMeshComponent;
struct FDNAIndexMapping;
struct FSharedRigRuntimeContext;
enum class EDNADataLayer: uint8;
/** An asset holding the data needed to generate/update/animate a RigLogic character
* It is imported from character's DNA file as a bit stream, and separated out it into runtime (behavior) and design-time chunks;
* Currently, the design-time part still loads the geometry, as it is needed for the skeletal mesh update; once SkeletalMeshDNAReader is
* fully implemented, it will be able to read the geometry directly from the SkeletalMesh and won't load it into this asset
**/
UCLASS(MinimalAPI, BlueprintType, meta = (DisplayName = "MetaHuman DNA data"))
class UDNAAsset : public UAssetUserData
{
GENERATED_BODY()
public:
UE_API UDNAAsset();
UE_API ~UDNAAsset();
#if WITH_EDITORONLY_DATA
UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSettings)
TObjectPtr<class UAssetImportData> AssetImportData;
#endif
UE_API TSharedPtr<IDNAReader> GetBehaviorReader();
#if WITH_EDITORONLY_DATA
UE_API TSharedPtr<IDNAReader> GetGeometryReader();
#endif
UPROPERTY(AssetRegistrySearchable)
FString DnaFileName;
/** In non-editor builds, the DNA source data will be unloaded to save memory after the runtime
* data has been initialized from it.
*
* Set this property to true to keep the DNA in memory, e.g. if you need to modify it at
* runtime. For most use cases, this shouldn't be needed.
**/
UPROPERTY(EditAnywhere, Category = RuntimeSettings)
bool bKeepDNAAfterInitialization;
/** Collection of runtime metadata related to a specific character.
* The value field is a FString and requires casting for a derived types.
**/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = RuntimeSettings)
TMap<FString, FString> MetaData;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = RuntimeSettings)
FRigLogicConfiguration RigLogicConfiguration;
#if WITH_EDITOR
UE_API void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
UE_API bool Init(const FString& Filename);
UE_API void Serialize(FArchive& Ar) override;
/** Used when importing behavior into archetype SkelMesh in the editor,
* and when updating SkeletalMesh runtime with GeneSplicer
**/
UE_API void SetBehaviorReader(TSharedPtr<IDNAReader> SourceDNAReader);
UE_API void SetGeometryReader(TSharedPtr<IDNAReader> SourceDNAReader);
/** Initialize this object for use at runtime from another instance that has already been
* initialized.
*
* Overwrites all member variables. Only data needed for runtime evaluation will be copied.
*
* Performs a shallow copy, so the runtime data is shared between the two instances and the
* memory cost of the copied UDNAAsset is very low.
*
* Note that the reference to the shared runtime data will be dropped if the source DNA is
* modified, so the two instances are effectively independent and can safely be modified or
* deleted without affecting the other.
**/
UE_API void InitializeForRuntimeFrom(UDNAAsset* Other);
UE_API TSharedPtr<FSharedRigRuntimeContext> GetRigRuntimeContext();
UE_API TSharedPtr<FDNAIndexMapping> GetDNAIndexMapping(const USkeleton* Skeleton, const USkeletalMesh* SkeletalMesh);
private:
friend struct FAnimNode_RigLogic;
friend struct FRigUnit_RigLogic;
UE_API void InvalidateRigRuntimeContext();
UE_API void InitializeRigRuntimeContext();
private:
struct FSkeletalMeshSkeletonToDNAIndexMappingKey
{
TWeakObjectPtr<const USkeletalMesh> SkeletalMesh;
TWeakObjectPtr<const USkeleton> Skeleton;
bool operator==(const FSkeletalMeshSkeletonToDNAIndexMappingKey& Other) const
{
return (SkeletalMesh == Other.SkeletalMesh) && (Skeleton == Other.Skeleton);
}
bool operator!=(const FSkeletalMeshSkeletonToDNAIndexMappingKey& Other) const
{
return !(*this == Other);
}
friend uint32 GetTypeHash(const FSkeletalMeshSkeletonToDNAIndexMappingKey& Instance)
{
return HashCombine(GetTypeHash(Instance.SkeletalMesh), GetTypeHash(Instance.Skeleton));
}
};
private:
// Synchronize DNA updates
FRWLock DNAUpdateLock;
// Synchronize Rig Runtime Context updates
FRWLock RigRuntimeContextUpdateLock;
// Synchronize DNA Index Mapping updates
FRWLock DNAIndexMappingUpdateLock;
/** Part of the .dna file needed for run-time execution of RigLogic;
**/
TSharedPtr<IDNAReader> BehaviorReader;
/** Part of the .dna file used design-time for updating SkeletalMesh geometry
**/
TSharedPtr<IDNAReader> GeometryReader;
/** Runtime data necessary for rig computations that is shared between
* multiple rig instances based on the same DNA.
**/
TSharedPtr<FSharedRigRuntimeContext> RigRuntimeContext;
/** Container for Skeleton <-> DNAAsset index mappings
* The mapping object owners will be the SkeletalMeshes, and periodic cleanups will
* ensure that dead objects are deleted from the map.
**/
TMap<FSkeletalMeshSkeletonToDNAIndexMappingKey, TSharedPtr<FDNAIndexMapping>> DNAIndexMappingContainer;
};
#undef UE_API