Files
2025-05-18 13:04:45 +08:00

235 lines
7.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GeometryTypes.h"
#include "UObject/NameTypes.h"
#include "Containers/Map.h"
#include "BoneIndices.h"
#include "BoneWeights.h" // UE::AnimationCore::MaxInlineBoneWeightCount;
#define UE_API DYNAMICMESH_API
// Forward declarations
class FProgressCancel;
namespace UE::Geometry
{
class FDynamicMesh3;
template<typename ParentType> class TDynamicVertexSkinWeightsAttribute;
using FDynamicMeshVertexSkinWeightsAttribute = TDynamicVertexSkinWeightsAttribute<FDynamicMesh3>;
}
namespace UE
{
namespace Geometry
{
/**
* Interface for getting the bone weights data. Should be subclassed and implemented if the bone weights data
* is not stored using the FDynamicMeshVertexSkinWeightsAttribute.
*/
template <typename BoneIndexType, typename BoneWeightType>
class TBoneWeightsDataSource
{
public:
virtual ~TBoneWeightsDataSource() = default;
/** Number of bones at a vertex. */
virtual int32 GetBoneNum(const int32 InVertexIDs) = 0;
/** Bone ID of the bone stored at an index Index, where Index is between 0 and GetBoneNum(InVertexIDs). */
virtual BoneIndexType GetBoneIndex(const int32 InVertexIDs, const int32 Index) = 0;
/** Bone weight of the bone stored at an index Index, where Index is between 0 and GetBoneNum(InVertexIDs). */
virtual BoneWeightType GetBoneWeight(const int32 InVertexIDs, const int32 Index) = 0;
/** Bone weight of the bone with Bone ID at a vertex. Must return 0 if bone has no influence. */
virtual BoneWeightType GetWeightOfBoneOnVertex(const int32 InVertexIDs, const BoneIndexType BoneIndex) = 0;
};
/**
* Collection of algorithms for smoothing bone weights. Bone weight data is fetched using the TBoneWeightsDataSource
* which is implemented by the caller to match their custom data storage of bone weights.
*
*
* Example usage:
*
* // Mesh whose bone weights we are smoothing
* FDynamicMesh SourceMesh = ... ;
*
* // The caller should create a subclass of TBoneWeightsDataSource that implements its interface
* TUniquePtr<TBoneWeightsDataSource> DataSource = ... ;
*
* // Setup the smoothing operator
* TSmoothBoneWeights SmoothBoneWeights(&SourceMesh, DataSource.Get());
*
* // Array of vertex IDs to apply smoothing to
* TArray<int32> VerticesToSmooth = ... ;
*
* // Run the operator
* const float SmoothStrength = 0.2;
* if (SmoothBoneWeights.Validate() == EOperationValidationResult::Ok)
* {
* for (const int32 VertexID : VerticesToSmooth)
* {
* TMap<BoneIndexType, BoneWeightType> FinalWeights;
* SmoothBoneWeights.SmoothWeightsAtVertex(VertexID, SmoothStrength, FinalWeights);
*
* // Use FinalWeights to modify your weights
* }
* }
*/
template <typename BoneIndexType, typename BoneWeightType>
class TSmoothBoneWeights
{
public:
//
// Optional Inputs
//
/** Set this to be able to cancel the running operation. */
FProgressCancel* Progress = nullptr;
/** Only consider the weights above the threshold. */
BoneWeightType MinimumWeightThreshold = (BoneWeightType) 0.0f;
protected:
const FDynamicMesh3* SourceMesh;
/** Data source supplied by the caller. */
TBoneWeightsDataSource<BoneIndexType, BoneWeightType>* DataSource = nullptr;
public:
/**
* @param InSourceMesh The mesh whose vertices contain bone weights data.
* @param InDataSource The bone weights data.
*/
TSmoothBoneWeights(const FDynamicMesh3* InSourceMesh, TBoneWeightsDataSource<BoneIndexType, BoneWeightType>* InDataSource = nullptr);
virtual ~TSmoothBoneWeights() = default;
/** @return EOperationValidationResult::Ok if we can apply operation, or error code if we cannot. */
virtual EOperationValidationResult Validate();
/**
* Compute smoothed weights at a vertex and return the result.
*
* @param VertexID The vertex whose weights we are smoothing.
* @param VertexFalloff The lerp value between the old and the new weight.
* @param OutFinalWeights The smoothed weights.
*
* @return true if the algorithm succeeds, false if it failed or was canceled by the user.
*/
virtual bool SmoothWeightsAtVertex(const int32 VertexID,
const BoneWeightType VertexFalloff,
TMap<BoneIndexType, BoneWeightType>& OutFinalWeights);
protected:
/** @return if true, abort the computation. */
virtual bool Cancelled();
};
/**
* Subclass of TSmoothBoneWeights for dealing with smoothing of bone weights stored in the
* FDynamicMeshVertexSkinWeightsAttribute.
*
*
* Example usage:
*
* // Mesh whose bone weights we are smoothing
* FDynamicMesh SourceMesh = ... ;
*
* // Setup the smoothing operator
* FSmoothDynamicMeshVertexSkinWeights SmoothBoneWeights(&SourceMesh, FSkeletalMeshAttributes::DefaultSkinWeightProfileName);
*
* // Array of vertex IDs to apply smoothing to
* TArray<int32> VerticesToSmooth = ... ;
*
* // Run the operator
* const float SmoothStrength = 0.2;
* if (SmoothBoneWeights.Validate() == EOperationValidationResult::Ok)
* {
* for (const int32 VertexID : VerticesToSmooth)
* {
* SmoothBoneWeights.SmoothWeightsAtVertex(VertexID, SmoothStrength);
* }
* }
*/
class FSmoothDynamicMeshVertexSkinWeights : public TSmoothBoneWeights<FBoneIndexType, float>
{
public:
using TSmoothBoneWeights<FBoneIndexType, float>::SmoothWeightsAtVertex;
/**
* During the smoothing, only use this number of influences per vertex. Prune the excess with the smallest influences
* and re-normalize.
*/
int32 MaxNumInfluences = UE::AnimationCore::MaxInlineBoneWeightCount;
/**
* @param InSourceMesh The mesh whose vertices contain bone weights data.
* @param InProfile The name of the skin weights profile containing the bone weights data.
*/
UE_API FSmoothDynamicMeshVertexSkinWeights(const FDynamicMesh3* InSourceMesh, const FName InProfile);
/**
* @param InSourceMesh The mesh whose vertices contain bone weights data.
* @param InAttribute The skin weight attribute containing the bone data.
*/
UE_API FSmoothDynamicMeshVertexSkinWeights(const FDynamicMesh3* InSourceMesh, FDynamicMeshVertexSkinWeightsAttribute* InAttribute);
virtual ~FSmoothDynamicMeshVertexSkinWeights() = default;
/** @return EOperationValidationResult::Ok if we can apply operation, or error code if we cannot. */
UE_API virtual EOperationValidationResult Validate() override;
/**
* Compute smoothed weights at a vertex and write the result into FDynamicMeshVertexSkinWeightsAttribute passed
* upon construction.
*
* @param VertexID The vertex whose weights we are smoothing.
* @param VertexFalloff The lerp value between the old and the new weight.
*
* @return true if the algorithm succeeds, false if it failed or was canceled by the user.
*/
UE_API bool SmoothWeightsAtVertex(const int32 VertexID, const float VertexFalloff);
/**
* Compute smoothed weights at vertices and neighbors within distance and write the result into
* FDynamicMeshVertexSkinWeightsAttribute passed upon construction.
*
* @param Vertices Array of vertex IDs we are smoothing weights for.
* @param Strength The lerp value between the old and the new weight.
* @param FloodFillUpToDistance For every vertex in Vertices, find vertices that are within FloodFillUpToDistance
* geodesic distance away and smooth them as well.
* @param NumIterations Number of times the smoothing will be run
*
* @return true if the algorithm succeeds, false if it failed or was canceled by the user.
*/
UE_API bool SmoothWeightsAtVerticesWithinDistance(const TArray<int32>& Vertices, const float Strength, const double FloodFillUpToDistance, const int32 NumIterations);
protected:
TUniquePtr<TBoneWeightsDataSource<FBoneIndexType, float>> SkinWeightsAttributeDataSource = nullptr;
FDynamicMeshVertexSkinWeightsAttribute* Attribute = nullptr;
};
} // end namespace UE::Geometry
} // end namespace UE
#undef UE_API