// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. #pragma once #include "CoreMinimal.h" #include "AnimNode_SlotCustom.h" #include "Data/OLSMaskAnimationData.h" #include "AnimNode_OLSMask.generated.h" /** * */ USTRUCT(BlueprintInternalUseOnly) struct FOLSWeightCurves { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) FName OverrideCurve = NAME_None; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) FName AdditiveCurve = NAME_None; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) FName LocalSpaceBlendCurve = NAME_None; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) FName MeshSpaceBlendCurve = NAME_None; }; USTRUCT(BlueprintInternalUseOnly) struct FOLSMaskSettings { GENERATED_BODY() UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) FName BlendProfileName = NAME_None; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) bool bUseMeshSpaceAdditive = false; UPROPERTY(BlueprintReadWrite, EditAnywhere, Category=Config) bool bShouldAddSlot = false; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config) TEnumAsByte CurveBlendOption = ECurveBlendOption::Override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config) FOLSWeightCurves WeightCurves; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config) FName SlotName = FAnimSlotGroup::DefaultSlotName; // Transient properties should be declared at the end UPROPERTY(Transient) TObjectPtr BlendProfile = nullptr; // Slot To Add If Needed FAnimNode_SlotCustom Slot; }; USTRUCT(BlueprintInternalUseOnly) struct FOLSPerBoneWeights { GENERATED_BODY() public: // Constructor to reserve memory for the arrays. // Assuming using Unreal's mannequin. FOLSPerBoneWeights() { LocalSpaceDesiredBoneBlendWeights.Reserve(32); MeshSpaceDesiredBoneBlendWeights.Reserve(32); LocalSpaceCurrentBoneBlendWeights.Reserve(32); MeshSpaceCurrentBoneBlendWeights.Reserve(32); LocalCurvePoseSourceIndices.Reserve(32); MeshCurvePoseSourceIndices.Reserve(32); } public: UPROPERTY() TArray LocalSpaceDesiredBoneBlendWeights; UPROPERTY() TArray MeshSpaceDesiredBoneBlendWeights; UPROPERTY() TArray LocalSpaceCurrentBoneBlendWeights; UPROPERTY() TArray MeshSpaceCurrentBoneBlendWeights; // Using a more memory-efficient representation for the curves TBaseBlendedCurve LocalCurvePoseSourceIndices; TBaseBlendedCurve MeshCurvePoseSourceIndices; }; USTRUCT(BlueprintInternalUseOnly) struct FOLSPerBoneBlendWeights { GENERATED_BODY() public: UPROPERTY() TArray PerBoneBlendWeights; }; USTRUCT(BlueprintInternalUseOnly) struct OLSANIMATION_API FAnimNode_OLSMask : public FAnimNode_Base { GENERATED_BODY() public: FAnimNode_OLSMask() : bHasRelevantPoses(false) , LODThreshold(INDEX_NONE) , RequiredBonesSerialNumber(0) { } // FAnimNode_Base interface virtual void Initialize_AnyThread(const FAnimationInitializeContext& context) override; virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& context) override; virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override; virtual void Evaluate_AnyThread(FPoseContext& output) override; virtual int32 GetLODThreshold() const override { return LODThreshold; } // Invalidate the cached per-bone blend weights from the skeleton void InvalidatePerBoneBlendWeights(); // Invalidates the cached bone data so it is recalculated the next time this node is updated void InvalidateCachedBoneData(); // Rebuild cache per bone blend weights from the skeleton void RebuildPerBoneBlendWeights(const USkeleton* skeleton); // Check whether per-bone blend weights are valid according to the skeleton (GUID check) bool ArePerBoneBlendWeightsValid(const USkeleton* skeleton) const; // Update cached data if required void UpdateCachedBoneData(const FBoneContainer& requiredBones, const USkeleton* skeleton); void UpdateBodyPartCachedBoneData(const FBoneContainer& requiredBones, const USkeleton* skeleton, const int32 maskIndex); void UpdateWeightsFromCurves(const USkeleton* skeleton, const FPoseContext& desiredCurvesPose, const int32 index); static void MakeAdditivePose(FPoseContext& outAdditivePose,FPoseContext& outBasePose, const bool shouldUseMeshSpaceAdditive); public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links) FPoseLink AnimationPose; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links) FPoseLink MaskPose; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links) FPoseLink BaseAdditivePose; UPROPERTY(EditDefaultsOnly,BlueprintReadWrite, Category=Config) TArray BlendMasks; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Runtime, meta=(PinShownByDefault)) TArray MaskBlendWeights; uint8 bHasRelevantPoses : 1; /* * Max LOD that this node is allowed to run * For example if you have LODThreadhold to be 2, it will run until LOD 2 (based on 0 index) * when the component LOD becomes 3, it will stop update/evaluate * currently transition would be issue and that has to be re-visited */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Performance, meta = (DisplayName = "LOD Threshold")) int32 LODThreshold = 0; // transient data to handle weight and target weight // this array changes based on required bones UPROPERTY() TArray AllBoneBlendWeights; protected: // Per-bone weights for the skeleton. Serialized as these are only relative to the skeleton, but can potentially // be regenerated at runtime if the GUIDs dont match. UPROPERTY() TArray PerBoneBlendWeights; // Guids for skeleton used to determine whether the PerBoneBlendWeights need rebuilding UPROPERTY() FGuid SkeletonGuid; // Guid for virtual bones used to determine whether the PerBoneBlendWeights need rebuilding UPROPERTY() FGuid VirtualBoneGuid; // Serial number of the required bones container uint16 RequiredBonesSerialNumber : 1; private: friend class UAnimGraphNode_KLSMask; };