UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
RBFInterpolator.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
7#include "Math/Quat.h"
8#include "Math/Rotator.h"
10#include "Math/Vector.h"
11#include "Misc/MemStack.h"
12#include "Templates/Function.h"
13#include "Templates/Tuple.h"
14
15/* A collection of distance metrics between two values of the same type */
17{
18 /* Returns the Euclidean (L2) distance between two coordinate vectors. */
19 static inline double Euclidean(const FVector& A, const FVector& B)
20 {
21 return FVector::Distance(A, B);
22 }
23
24 /* Returns the Manhattan (L1), or Taxi-cab distance between two coordinate vectors. */
25 static inline double Manhattan(const FVector& A, const FVector& B)
26 {
27 FVector AbsDiff = (A - B).GetAbs();
28 return AbsDiff.X + AbsDiff.Y + AbsDiff.Z;
29 }
30
31 /* Returns the arc length between two unit vectors (i.e. the distance between two
32 points on a unit sphere, traveling along the surface of the sphere) */
33 static inline double ArcLength(const FVector& A, const FVector B)
34 {
35 return FMath::Acos(A.GetSafeNormal() | B.GetSafeNormal());
36 }
37
38
39 /* Returns a straight-up Euclidean distance between two rotation values expressed
40 in radians.
41 */
42 static inline double Euclidean(const FRotator& A, const FRotator& B)
43 {
50 }
51
52 /* Returns the arc-length distance, on a unit sphere, between two rotation vectors.
53 */
54 static inline double ArcLength(const FRotator& A, const FRotator& B)
55 {
56 return FMath::Acos(A.Vector() | B.Vector());
57 }
58
59 /* Returns the Euclidean (L2) distance between two quaternion values expressed.
60 */
61 static inline double Euclidean(const FQuat& A, const FQuat& B)
62 {
63 return (A - B).Size();
64 }
65
66 /* Returns the arc-length distance, on a unit sphere, between two quaternions.
67 */
68 static inline double ArcLength(const FQuat& A, const FQuat& B)
69 {
70 return A.GetNormalized().AngularDistance(B.GetNormalized());
71 }
72
73 /* Returns the swing arc length distance between two quaternions, using a specific
74 twist basis vector as reference.
75 */
76 static inline double SwingAngle(const FQuat& A, const FQuat& B, const FVector& TwistAxis)
77 {
79 A.ToSwingTwist(TwistAxis, ASwing, DummyTwist);
80 B.ToSwingTwist(TwistAxis, BSwing, DummyTwist);
81 return ASwing.AngularDistance(BSwing);
82 }
83
84 /* Returns the twist arc length distance between two quaternions, using a specific
85 twist basis vector as reference.
86 */
87 static inline double TwistAngle(const FQuat& A, const FQuat& B, const FVector& TwistAxis)
88 {
89 return FMath::Abs(A.GetTwistAngle(TwistAxis) - B.GetTwistAngle(TwistAxis));
90 }
91}
92
93
94/* A collection of smoothing kernels, all of which map the input of zero to 1.0 and
95 all values on either side as monotonically decreasing as they move away from zero.
96 The width of the falloff can be specified using the Sigma parameter.
97 */
98namespace RBFKernel
99{
100 /* A simple linear falloff, clamping at zero out when the norm of Value exceeds Sigma */
101 static inline float Linear(float Value, float Sigma)
102 {
103 return (Sigma - FMath::Clamp(Value, 0.0f, Sigma)) / Sigma;
104 }
105
106 /* A gaussian falloff */
107 static inline float Gaussian(float Value, float Sigma)
108 {
109 return FMath::Exp(-Value * FMath::Square(1.0f / Sigma));
110 }
111
112 /* An exponential falloff with a sharp peak */
113 static inline float Exponential(float Value, float Sigma)
114 {
115 return FMath::Exp(-2.0f * Value / Sigma);
116 }
117
118 /* A cubic falloff, with identical clamping behavior to the linear falloff,
119 but with a smooth peak */
120 static inline float Cubic(float Value, float Sigma)
121 {
122 Value /= Sigma;
123 return FMath::Max(1.f - (Value * Value * Value), 0.f);
124 }
125
126 /* A quintic falloff, with identical clamping behavior to the linear falloff,
127 but with a flatter peak than cubic */
128 static inline float Quintic(float Value, float Sigma)
129 {
130 Value /= Sigma;
131 return FMath::Max(1.f - FMath::Pow(Value, 5.0f), 0.f);
132 }
133}
134
135
136// An implementation detail for the RBF interpolator to hide the use of Eigen from components
137// outside AnimGraphRuntime.
139{
140protected:
142
143 // A square matrix of the solved coefficients.
144public:
146 bool bIsValid = false;
147};
148
149
150template<typename T>
152 : public FRBFInterpolatorBase
153{
154public:
155 using WeightFuncT = TFunction<float(const T& A, const T& B)>;
156
157 TRBFInterpolator() = default;
158
159 /* Construct an RBF interpolator, taking in a set of sparse nodes and a symmetric weighing
160 function that computes the distance between two nodes, and, optionally, smooths
161 the distance with a smoothing kernel.
162 */
164 const TArrayView<T>& InNodes,
166 : Nodes(InNodes)
167 , WeightFunc(InWeightFunc)
168 {
169 MakeUpperKernel();
170 }
171
176
177 /* Given a value, compute the weight values to use to calculate each node's contribution
178 to that value's location.
179 */
180 template<typename U, typename InAllocator>
183 const U& Value,
184 bool bClip = true,
185 bool bNormalize = false) const
186 {
187 int NumNodes = Nodes.Num();
188
189 if (!bIsValid)
190 {
191 OutWeights.Init(0.0f, NumNodes);
192 return;
193 }
194
195 if (NumNodes > 1)
196 {
198 ValueWeights.SetNum(Nodes.Num());
199
200 for (int32 i = 0; i < NumNodes; i++)
201 {
202 ValueWeights[i] = WeightFunc(Value, Nodes[i]);
203 }
204
205 OutWeights.Reset(NumNodes);
206 for (int32 i = 0; i < NumNodes; i++)
207 {
208 const float* C = &Coeffs[i * NumNodes];
209 float W = 0.0f;
210
211 for (int32 j = 0; j < NumNodes; j++)
212 {
213 W += C[j] * ValueWeights[j];
214 }
215
216 OutWeights.Add(W);
217 }
218
219
220 if (bNormalize)
221 {
222 // Clip here behaves differently than it does when no normalization
223 // is taking place. Instead of clipping blindly, we rescale the values based
224 // on the minimum value and then use the normalization to bring the values
225 // within the 0-1 range.
226 if (bClip)
227 {
228 float MaxNegative = 0.0f;
229 for (int32 i = 0; i < NumNodes; i++)
230 {
231 if (OutWeights[i] < MaxNegative)
233 }
234 for (int32 i = 0; i < NumNodes; i++)
235 {
237 }
238 }
239
240 float TotalWeight = 0.0f;
241 for (int32 i = 0; i < NumNodes; i++)
242 {
243 TotalWeight += OutWeights[i];
244 }
245 for (int32 i = 0; i < NumNodes; i++)
246 {
247 // Clamp to clear up any precision issues. This may make the weights not
248 // quite add up to 1.0, but that should be sufficient for our needs.
249 OutWeights[i] = FMath::Clamp(OutWeights[i] / TotalWeight, 0.0f, 1.0f);
250 }
251 }
252 else if (bClip)
253 {
254 // This can easily happen when the value being interpolated is outside of the
255 // convex hull bounded by the nodes, resulting in an extrapolation.
256 for (int32 i = 0; i < NumNodes; i++)
257 {
258 OutWeights[i] = FMath::Clamp(OutWeights[i], 0.0f, 1.0f);
259 }
260 }
261 }
262 else if (NumNodes == 1)
263 {
264 OutWeights.Reset(1);
265 OutWeights.Add(1);
266 }
267 else
268 {
269 OutWeights.Reset(0);
270 }
271 }
272
273 // Returns a list of integer pairs indicating which distinct pair of nodes have the same
274 // weight as a pair of the same node. These result in an ill-formed coefficient matrix
275 // which kills the interpolation. The user can then either simply remove one of the pairs
276 // and retry, or warn the user that they have an invalid setup.
278 const TArrayView<T>& InNodes,
281 )
282 {
283 int NumNodes = InNodes.Num();
284 if (NumNodes < 2)
285 {
286 return false;
287 }
288
289 // One of the assumptions we make, is that the smoothing function is symmetric,
290 // hence we can use the weight between the same node as the functional equivalent
291 // of the identity weight between any two nodes.
293
294 OutInvalidPairs.Empty();
295 for (int32 i = 0; i < (NumNodes - 1); i++)
296 {
297 for (int32 j = i + 1; j < NumNodes; j++)
298 {
299 float Weight = InWeightFunc(InNodes[i], InNodes[j]);
300
301 // Don't use the default ULP, but be a little more cautious, since a matrix
302 // inversion can lose a chunk of float precision.
304 {
305 OutInvalidPairs.Add(MakeTuple(i, j));
306 }
307 }
308 }
309 return OutInvalidPairs.Num() != 0;
310 }
311
312private:
313 void MakeUpperKernel()
314 {
315 // If there are less than two nodes, nothing to do, since the interpolated value
316 // will be the same across the entire space. This is handled in Interpolate().
317 int32 NumNodes = Nodes.Num();
318 if (NumNodes < 2)
319 {
320 bIsValid = true;
321 return;
322 }
323
324 // Compute the upper diagonal of the target kernel for solving the weight coefficients.
326 UpperKernel.Reserve(NumNodes * (NumNodes - 1) / 2);
327
328 // We need to include the diagonal itself, since we can't guarantee that the weight
329 // function returns 1.0 for nodes of the same coordinates.
330 for (int32 i = 0; i < NumNodes; i++)
331 {
332 // PVS thinks the use of 'j = i' might be an bug. It is not.
333 for (int32 j = i; j < NumNodes; j++) //-V791
334 {
335 UpperKernel.Add(WeightFunc(Nodes[i], Nodes[j]));
336 }
337 }
338
340 }
341
342
343 TArrayView<T> Nodes;
344 WeightFuncT WeightFunc;
345};
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define FVector
Definition IOSSystemIncludes.h:8
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
constexpr TTuple< std::decay_t< Types >... > MakeTuple(Types &&... Args)
Definition Tuple.h:794
uint32 Size
Definition VulkanMemory.cpp:4034
Definition RBFInterpolator.h:139
TArray< float > Coeffs
Definition RBFInterpolator.h:145
bool bIsValid
Definition RBFInterpolator.h:146
ANIMGRAPHRUNTIME_API bool SetUpperKernel(const TArrayView< float > &UpperKernel, int32 Size)
Definition RBFInterpolator.cpp:24
Definition ArrayView.h:139
UE_FORCEINLINE_HINT constexpr SizeType Num() const
Definition ArrayView.h:380
Definition Array.h:670
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition RBFInterpolator.h:153
TRBFInterpolator< T > & operator=(TRBFInterpolator< T > &&)=default
TRBFInterpolator< T > & operator=(const TRBFInterpolator< T > &)=default
TRBFInterpolator(TRBFInterpolator< T > &&)=default
void Interpolate(TArray< float, InAllocator > &OutWeights, const U &Value, bool bClip=true, bool bNormalize=false) const
Definition RBFInterpolator.h:181
static bool GetIdenticalNodePairs(const TArrayView< T > &InNodes, WeightFuncT InWeightFunc, TArray< TTuple< int, int > > &OutInvalidPairs)
Definition RBFInterpolator.h:277
TRBFInterpolator(const TArrayView< T > &InNodes, WeightFuncT InWeightFunc)
Definition RBFInterpolator.h:163
TFunction< float(const T &A, const T &B)> WeightFuncT
Definition RBFInterpolator.h:155
TRBFInterpolator(const TRBFInterpolator< T > &)=default
TRBFInterpolator()=default
Definition RBFInterpolator.h:17
Definition RBFInterpolator.h:99
static constexpr UE_FORCEINLINE_HINT auto DegreesToRadians(T const &DegVal) -> decltype(DegVal *(UE_PI/180.f))
Definition UnrealMathUtility.h:871
static UE_FORCEINLINE_HINT bool IsNearlyEqualByULP(float A, float B, int32 MaxUlps=4)
Definition UnrealMathUtility.h:486
static constexpr UE_FORCEINLINE_HINT T Square(const T A)
Definition UnrealMathUtility.h:578
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
Definition Tuple.h:652
CORE_API void ToSwingTwist(const TVector< T > &InTwistAxis, TQuat< T > &OutSwing, TQuat< T > &OutTwist) const
Definition UnrealMath.cpp:793
static UE_FORCEINLINE_HINT double Distance(const TVector< double > &V1, const TVector< double > &V2)
Definition Vector.h:1018
T X
Definition Vector.h:62