304 lines
12 KiB
C++
304 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "GeometryTypes.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "TransformTypes.h"
|
|
#include "BoneWeights.h" // UE::AnimationCore::MaxInlineBoneWeightCount
|
|
|
|
#define UE_API DYNAMICMESH_API
|
|
|
|
// Forward declarations
|
|
class FProgressCancel;
|
|
|
|
|
|
namespace UE::Geometry
|
|
{
|
|
class FDynamicMesh3;
|
|
template<typename MeshType> class TMeshAABBTree3;
|
|
typedef TMeshAABBTree3<FDynamicMesh3> FDynamicMeshAABBTree3;
|
|
class FMeshNormals;
|
|
}
|
|
|
|
|
|
namespace UE
|
|
{
|
|
namespace Geometry
|
|
{
|
|
/**
|
|
* Transfer bone weights from one mesh (source) to another (target). Uses the dynamic mesh bone attributes to reindex
|
|
* the bone indices of the transferred weights from the source to the target skeletons. If both meshes have identical
|
|
* bone name attributes, then reindexing is skipped.
|
|
*
|
|
* During the reindexing, if a weighted bone in the source skeleton is not present in the target skeleton, then the
|
|
* weight is not transferred (skipped), and an error is printed to the console. For best results, the target skeleton
|
|
* should be a superset of all the bones that are indexed by the transferred weights.
|
|
*
|
|
*
|
|
* Example usage:
|
|
*
|
|
* FDynamicMesh SourceMesh = ...; // Mesh we transferring weights from. Must have bone attributes.
|
|
* FDynamicMesh TargetMesh = ...; // Mesh we are transferring weights to.
|
|
*
|
|
* FTransferBoneWeights TransferBoneWeights(&SourceMesh, FSkeletalMeshAttributes::DefaultSkinWeightProfileName);
|
|
*
|
|
* // Optionally, transform the target mesh. This is useful when you want to align the two meshes in world space.
|
|
* TransferBoneWeights.TargetToWorld = ...;
|
|
*
|
|
* // When transferring weights from a dynamic mesh with bone attributes to a dynamic mesh without bone attributes,
|
|
* // first copy over the bone attributes from the source to the target.
|
|
* if (!TargetMesh.HasAttributes() || !TargetMesh.Attributes()->HasBones())
|
|
* {
|
|
* TargetMesh.EnableAttributes();
|
|
* TargetMesh.Attributes()->CopyBoneAttributes(*SourceMesh.Attributes());
|
|
* }
|
|
*
|
|
* // Set the transfer method.
|
|
* TransferBoneWeights.TransferMethod = ETransferBoneWeightsMethod::...;
|
|
*
|
|
* // if ETransferBoneWeightsMethod::ClosestPointOnSurface is used and you simply want to copy weights over from the
|
|
* // closest points then set the radius and normal threshold to -1 (default).
|
|
* TransferBoneWeights.SearchRadius = -1
|
|
* TransferBoneWeights.NormalThreshold = -1
|
|
*
|
|
* // if ETransferBoneWeightsMethod::InpaintWeights is used then additionally set the radius and normal parameters
|
|
* TransferBoneWeights.SearchRadius = ... // Good estimate is to use a small value (0.05) of the bounding box radius
|
|
* TransferBoneWeights.NormalThreshold = ... // 30 degrees (0.52 rad) works well in practice
|
|
*
|
|
* if (TransferBoneWeights.Validate() == EOperationValidationResult::Ok)
|
|
* {
|
|
* TransferBoneWeights.TransferWeightsToMesh(TargetMesh, FSkeletalMeshAttributes::DefaultSkinWeightProfileName);
|
|
* }
|
|
*
|
|
* // Alternatively if you don't want to use FDynamicMesh3 to represent your target mesh you can transfer weights to
|
|
* // to each point separately by calling
|
|
* if (TransferBoneWeights.Validate() == EOperationValidationResult::Ok)
|
|
* {
|
|
* for each Point:
|
|
* FBoneWeights Weights;
|
|
* TransferBoneWeights.TransferWeightsToPoint(Weights, Point);
|
|
* }
|
|
*
|
|
* // After the transfer you can check which target mesh vertices had the weight transferred directly from the source mesh
|
|
* // via the FTransferBoneWeights:MatchedVertices array
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
class FTransferBoneWeights
|
|
{
|
|
public:
|
|
|
|
enum class ETransferBoneWeightsMethod : uint8
|
|
{
|
|
// For every vertex on the target mesh, find the closest point on the surface of the source mesh. If that point
|
|
// is within the SearchRadius, and their normals differ by less than the NormalThreshold, then we directly copy
|
|
// the weights from the source point to the target mesh vertex.
|
|
ClosestPointOnSurface = 0,
|
|
|
|
// Same as the ClosestPointOnSurface but for all the vertices we didn't copy the weights directly, automatically
|
|
// compute the smooth weights.
|
|
InpaintWeights = 1
|
|
};
|
|
|
|
//
|
|
// Optional Inputs
|
|
//
|
|
|
|
/** Set this to be able to cancel the running operation. */
|
|
FProgressCancel* Progress = nullptr;
|
|
|
|
/** Enable/disable multi-threading. */
|
|
bool bUseParallel = true;
|
|
|
|
/** The transfer method to compute the bone weights. */
|
|
ETransferBoneWeightsMethod TransferMethod = ETransferBoneWeightsMethod::ClosestPointOnSurface;
|
|
|
|
/** Transform applied to the input target mesh or target point before transfer. */
|
|
FTransformSRT3d TargetToWorld = FTransformSRT3d::Identity();
|
|
|
|
/**
|
|
* Completely ignore the source and target mesh bone attributes when transferring weights from one dynamic mesh to another.
|
|
* This skips re-indexing and simply copies skin weights over. Use with caution.
|
|
*/
|
|
bool bIgnoreBoneAttributes = false;
|
|
|
|
//
|
|
// Optional Inputs for ETransferBoneWeightsMethod::InpaintWeights method
|
|
//
|
|
|
|
/** Radius for searching the closest point. If negative, all points are considered. */
|
|
double SearchRadius = -1;
|
|
|
|
/**
|
|
* Maximum angle (in radians) difference between target and source point normals to be considred a match.
|
|
* If negative, normals are ignored.
|
|
*/
|
|
double NormalThreshold = -1;
|
|
|
|
/**
|
|
* If true, when the closest point doesn't pass the normal threshold test, will try again with a flipped normal.
|
|
* This helps with layered meshes where the "inner" and "outer" layers are close to each other but whose normals
|
|
* are pointing in the opposite directions.
|
|
*/
|
|
bool LayeredMeshSupport = false;
|
|
|
|
/** The number of optional post-processing smoothing iterations applied to the vertices without the match. */
|
|
int32 NumSmoothingIterations = 0;
|
|
|
|
/** The strength of each post-processing smoothing iteration. */
|
|
float SmoothingStrength = 0.0f;
|
|
|
|
/** If true, will use the intrinsic Delaunay mesh to construct sparse Cotangent Laplacian matrix. */
|
|
bool bUseIntrinsicLaplacian = false;
|
|
|
|
/**
|
|
* During the transfer, only use this number of influences per vertex. Prune the excess with the smallest influences
|
|
* and re-normalize.
|
|
*/
|
|
int32 MaxNumInfluences = UE::AnimationCore::MaxInlineBoneWeightCount;
|
|
|
|
/**
|
|
* Optional mask where if ForceInpaint[VertexID] != 0 we want to force the weights for the vertex to be computed
|
|
* automatically.
|
|
*
|
|
* @note Only used when TransferMethod == ETransferBoneWeightsMethod::InpaintWeights.
|
|
* The size must be equal to the InTargetMesh.MaxVertexID(), otherwise the mask is ignored.
|
|
*/
|
|
TArray<float> ForceInpaint;
|
|
|
|
/**
|
|
* Alternatively, if the mask is stored as a target mesh weight map attribute, specify its name. Will be ignored if
|
|
* the ForceInpaint array is not empty and valid.
|
|
*/
|
|
FName ForceInpaintWeightMapName = NAME_None;
|
|
|
|
/**
|
|
* Optional subset of target mesh vertices to transfer weights to.
|
|
* If left empty, skin weights will be transferred to all target mesh vertices.
|
|
*/
|
|
TArray<int32> TargetVerticesSubset;
|
|
|
|
//
|
|
// Outputs
|
|
//
|
|
|
|
/** MatchedVertices[VertexID] is set to true for a target mesh vertex ID with a match found, false otherwise. */
|
|
TArray<bool> MatchedVertices;
|
|
|
|
protected:
|
|
|
|
/** Source mesh we are transfering weights from. */
|
|
const FDynamicMesh3* SourceMesh;
|
|
|
|
/** The name of the source mesh skinning profile name. */
|
|
FName SourceProfileName;
|
|
|
|
/**
|
|
* The caller can optionally specify the source mesh BVH in case this operator is run on multiple target meshes
|
|
* while the source mesh remains the same. Otherwise BVH tree will be computed.
|
|
*/
|
|
const FDynamicMeshAABBTree3* SourceBVH = nullptr;
|
|
|
|
/** If the caller doesn't pass BVH for the source mesh then we compute one. */
|
|
TUniquePtr<FDynamicMeshAABBTree3> InternalSourceBVH;
|
|
|
|
/** If the source mesh doesn't have per-vertex normals then compute them */
|
|
TUniquePtr<FMeshNormals> InternalSourceMeshNormals;
|
|
|
|
public:
|
|
|
|
/**
|
|
* @param InSourceMesh The mesh we are transferring weights from
|
|
* @param InSourceProfileName The profile name of the skin weight attribute we are transferring weights from.
|
|
* @param SourceBVH Optional source mesh BVH. If not provided, one will be computed internally.
|
|
*
|
|
* @note Assumes that the InSourceMesh has bone attributes, use bIgnoreBoneAttributes flag to ignore the bone
|
|
* attributes and skip re-indexing.
|
|
*/
|
|
UE_API FTransferBoneWeights(const FDynamicMesh3* InSourceMesh,
|
|
const FName& InSourceProfileName,
|
|
const FDynamicMeshAABBTree3* SourceBVH = nullptr);
|
|
|
|
UE_API virtual ~FTransferBoneWeights();
|
|
|
|
/**
|
|
* @return EOperationValidationResult::Ok if we can apply operation, or error code if we cannot.
|
|
*/
|
|
UE_API virtual EOperationValidationResult Validate();
|
|
|
|
/**
|
|
* Transfer the bone weights from the source mesh to the given target mesh and store the result in the skin weight
|
|
* attribute with the given profile name.
|
|
*
|
|
* @param InOutTargetMesh Target mesh we are transfering weights into
|
|
* @param InTargetProfileName Skin weight profile name we are writing into. If the profile with that name exists,
|
|
* then the data will be overwritten, otherwise a new attribute will be created.
|
|
*
|
|
* @return true if the algorithm succeeds, false if it failed or was canceled by the user.
|
|
*
|
|
* @note Assumes that the InOutTargetMesh has bone attributes, use bIgnoreBoneAttributes flag to ignore the bone
|
|
* attributes and skip re-indexing.
|
|
*/
|
|
UE_API virtual bool TransferWeightsToMesh(FDynamicMesh3& InOutTargetMesh, const FName& InTargetProfileName);
|
|
|
|
|
|
/**
|
|
* Compute the bone weights for a given point using the ETransferBoneWeightsMethod::ClosestPointOnSurface algorithm.
|
|
*
|
|
* @param OutWeights Bone weight computed for the input transformed point.
|
|
* @param InPoint Point for which we are computing the bone weight.
|
|
* @param TargetBoneToIndex Optional map from the bone names to the bone indices of the target skeleton.
|
|
* If null, the bone indices of the skinning weights will not be re-indexed after the transfer.
|
|
* @param InNormal Normal at the input point. Should be set if NormalThreshold >= 0.
|
|
*
|
|
* @return true if the algorithm succeeds, false if it failed to find the matching point or was canceled by the user.
|
|
*/
|
|
UE_API virtual bool TransferWeightsToPoint(UE::AnimationCore::FBoneWeights& OutWeights,
|
|
const FVector3d& InPoint,
|
|
const TMap<FName, uint16>* TargetBoneToIndex = nullptr,
|
|
const FVector3f& InNormal = FVector3f::Zero());
|
|
|
|
/**
|
|
* Compute the bone weights for a given point using the ETransferBoneWeightsMethod::ClosestPointOnSurface algorithm.
|
|
*
|
|
* @param OutBones Array of bone indices. Array size is equal to the number of bone influences.
|
|
* @param OutWeights Array of bone weights. Array size is equal to the number of bone influences.
|
|
* @param InPoint Point for which we are computing the bone weight.
|
|
* @param TargetBoneToIndex Optional map from the bone names to the bone indices of the target skeleton.
|
|
* If null, the bone indices of the skinning weights will not be re-indexed after the transfer.
|
|
* @param InNormal Normal at the input point. Should be set if NormalThreshold >= 0.
|
|
*
|
|
* @return true if the algorithm succeeds, false if it failed to find the matching point or was canceled by the user.
|
|
*
|
|
* @note Add specialization in the source file for template types you want to use.
|
|
* Currently supporting int for BoneIndexType and float for BoneFloatWeightType, PosVectorType, NormalVectorType
|
|
*/
|
|
template<typename BoneIndexType, typename BoneFloatWeightType, typename PosVectorType, typename NormalVectorType = float>
|
|
bool TransferWeightsToPoint(TArray<BoneIndexType>& OutBones,
|
|
TArray<BoneFloatWeightType>& OutWeights,
|
|
const UE::Math::TVector<PosVectorType>& InPoint,
|
|
const TMap<FName, uint16>* TargetBoneToIndex = nullptr,
|
|
const UE::Math::TVector<NormalVectorType>& InNormal = UE::Math::TVector<NormalVectorType>::Zero());
|
|
protected:
|
|
|
|
/** @return if true, abort the computation. */
|
|
UE_API virtual bool Cancelled();
|
|
|
|
/**
|
|
* Find the closest point on the surface of the source mesh and return the ID of the triangle containing it and its
|
|
* barycentric coordinates.
|
|
*
|
|
* @return true if point is found, false otherwise
|
|
*/
|
|
UE_API bool FindClosestPointOnSourceSurface(const FVector3d& InPoint, const FTransformSRT3d& InToWorld, int32& OutTriID, FVector3d& OutBary);
|
|
|
|
};
|
|
|
|
} // end namespace UE::Geometry
|
|
} // end namespace UE
|
|
|
|
#undef UE_API
|