// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include "UObject/ObjectMacros.h" #include "PointWeightMap.generated.h" /** The possible targets for a physical mesh point weight map. */ UENUM() enum class EWeightMapTargetCommon : uint8 { None = 0, // None, should always be zero MaxDistance, // The distance that each vertex can move away from its reference (skinned) position BackstopDistance, // Distance along the plane of the surface that the particles can travel (separation constraint) BackstopRadius, // Radius of movement to allow for backstop movement AnimDriveStiffness, // Strength of anim drive per-particle (spring driving particle back to skinned location AnimDriveDamping_DEPRECATED UMETA(Hidden), // Chaos onlyweightmap, deprecated from the common declaration FirstUserTarget, // Users can add additional targets between this range. LastUserTarget = 200, // Add additional common targets after here. TetherEndsMask }; /** * A mask is simply some storage for a physical mesh parameter painted onto clothing. * Used in the editor for users to paint onto and then target to a parameter, which * is then later applied to a phys mesh */ USTRUCT() struct FPointWeightMap { GENERATED_BODY(); FPointWeightMap() #if WITH_EDITORONLY_DATA : Name(NAME_None) , CurrentTarget((uint8)EWeightMapTargetCommon::None) , bEnabled(false) #endif {} explicit FPointWeightMap(int32 NumPoints, float Value = 0.f) #if WITH_EDITORONLY_DATA : Name(NAME_None) , CurrentTarget((uint8)EWeightMapTargetCommon::None) , bEnabled(false) #endif { Values.Init(Value, NumPoints); } explicit FPointWeightMap(const TConstArrayView& InValues) : Values(InValues) #if WITH_EDITORONLY_DATA , Name(NAME_None) , CurrentTarget((uint8)EWeightMapTargetCommon::None) , bEnabled(false) #endif {} FPointWeightMap(const TConstArrayView& InValues, float Offset, float Scale) #if WITH_EDITORONLY_DATA : Name(NAME_None) , CurrentTarget((uint8)EWeightMapTargetCommon::None) , bEnabled(false) #endif { const int32 NumPoints = InValues.Num(); Values.SetNumUninitialized(NumPoints); for (int32 Index = 0; Index < NumPoints; ++Index) { Values[Index] = Offset + Scale * InValues[Index]; } } ~FPointWeightMap() {} /** * Reset this map to the specified number of points, and set all the values to zero. * @param NumPoints the number of points to initialize the map with. */ void Initialize(const int32 NumPoints) { Values.Init(0.f, NumPoints); #if WITH_EDITORONLY_DATA CurrentTarget = (uint8)EWeightMapTargetCommon::None; bEnabled = false; #endif } /** * Initialize a weight map from another weight map while enabling and setting a new target. * @param Source the source weight map to copy the values from. * @param Target the new weight map target. */ template < typename T UE_REQUIRES(std::is_enum_v || std::is_arithmetic_v) > void Initialize(const FPointWeightMap& Source, T Target) { Values = Source.Values; #if WITH_EDITORONLY_DATA CurrentTarget = (uint8)Target; bEnabled = true; #endif } /** Empty this map of any values. */ void Empty() { Values.Empty(); } /** Return the number of values in this map. */ int32 Num() const { return Values.Num(); } /** * Return the current float value for the requested point. * @param Index the value/point index to retrieve, must be within range or it will assert. */ const float& operator[](int32 Index) const { return Values[Index]; } /** * Return the current float value for the requested point. * @param Index the value/point index to retrieve, must be within range or it will assert. */ float& operator[](int32 Index) { return Values[Index]; } /** * Get a value from the map, or return 0 if the index is out of bounds. * @param Index the value/point index to retrieve */ float GetValue(int32 Index) const { return Values.IsValidIndex(Index) ? Values[Index] : 0.f; } /** * Set a value in the map checking first whether the index is within bounds. * @param Index the value/point index to set * @param Value the value to set */ void SetValue(int32 Index, float Value) { if (Values.IsValidIndex(Index)) Values[Index] = Value; } /** * Return whether the specified point weight is below (or equal) to the specified threshold. * @param Index the value/point index to retrieve * @param Threshold, the value threshold to test against for. */ bool IsBelowThreshold(const int32 Index, const float Threshold=0.1f) const { return Values.IsValidIndex(Index) && Values[Index] <= Threshold; } /** Return whether at least one of the specified triangle points has weight below (or equal) to the specified @param Threshold. */ bool AreAnyBelowThreshold(const int32 Index0, const int32 Index1, const int32 Index2, const float Threshold=0.1f) const { return IsBelowThreshold(Index0, Threshold) || IsBelowThreshold(Index1, Threshold) || IsBelowThreshold(Index2, Threshold); } /** Return whether all of the specified triangle points have weight below (or equal) to the specified @param Threshold. */ bool AreAllBelowThreshold(const int32 Index0, const int32 Index1, const int32 Index2, const float Threshold=0.1f) const { return IsBelowThreshold(Index0, Threshold) && IsBelowThreshold(Index1, Threshold) && IsBelowThreshold(Index2, Threshold); } /** Return whether all points' values are zero. */ bool IsZeroed() const { return !Values.FindByPredicate([](const float& Value) { return Value != 0.f; }); } /** Calculates Min/Max values based on values. */ void CalcRanges(float& MinValue, float& MaxValue) { MinValue = FMath::Min(Values); MaxValue = FMath::Max(Values); } /** The actual values stored in the mask */ UPROPERTY() TArray Values; #if WITH_EDITORONLY_DATA /** Name of the mask, mainly for users to differentiate */ UPROPERTY() FName Name; /** The currently targeted parameter for the mask, @seealso EWeightMapTargetCommon */ UPROPERTY() uint8 CurrentTarget; /** Whether this mask is enabled and able to effect final mesh values */ UPROPERTY() bool bEnabled; #endif };