170 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			170 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System.Runtime.CompilerServices; | ||
|  | using Unity.IL2CPP.CompilerServices; | ||
|  | 
 | ||
|  | namespace Unity.Mathematics | ||
|  | { | ||
|  |     // SVD algorithm as described in: | ||
|  |     // Computing the singular value decomposition of 3x3 matrices with minimal branching and elementary floating point operations, | ||
|  |     // A.McAdams, A.Selle, R.Tamstorf, J.Teran and E.Sifakis, University of Wisconsin - Madison technical report TR1690, May 2011 | ||
|  |     [Il2CppEagerStaticClassConstruction] | ||
|  |     static public class svd | ||
|  |     { | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static void condSwap(bool c, ref float x, ref float y) | ||
|  |         { | ||
|  |             var tmp = x; | ||
|  |             x = math.select(x, y, c); | ||
|  |             y = math.select(y, tmp, c); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static void condNegSwap(bool c, ref float3 x, ref float3 y) | ||
|  |         { | ||
|  |             var tmp = -x; | ||
|  |             x = math.select(x, y, c); | ||
|  |             y = math.select(y, tmp, c); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static quaternion condNegSwapQuat(bool c, quaternion q, float4 mask) | ||
|  |         { | ||
|  |             const float halfSqrt2 = 0.707106781186548f; | ||
|  |             return math.mul(q, math.select(quaternion.identity.value, mask * halfSqrt2, c)); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static void sortSingularValues(ref float3x3 b, ref quaternion v) | ||
|  |         { | ||
|  |             var l0 = math.lengthsq(b.c0); | ||
|  |             var l1 = math.lengthsq(b.c1); | ||
|  |             var l2 = math.lengthsq(b.c2); | ||
|  | 
 | ||
|  |             var c = l0 < l1; | ||
|  |             condNegSwap(c, ref b.c0, ref b.c1); | ||
|  |             v = condNegSwapQuat(c, v, math.float4(0f, 0f, 1f, 1f)); | ||
|  |             condSwap(c, ref l0, ref l1); | ||
|  | 
 | ||
|  |             c = l0 < l2; | ||
|  |             condNegSwap(c, ref b.c0, ref b.c2); | ||
|  |             v = condNegSwapQuat(c, v, math.float4(0f, -1f, 0f, 1f)); | ||
|  |             condSwap(c, ref l0, ref l2); | ||
|  | 
 | ||
|  |             c = l1 < l2; | ||
|  |             condNegSwap(c, ref b.c1, ref b.c2); | ||
|  |             v = condNegSwapQuat(c, v, math.float4(1f, 0f, 0f, 1f)); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static quaternion approxGivensQuat(float3 pq, float4 mask) | ||
|  |         { | ||
|  |             const float c8 = 0.923879532511287f; // cos(pi/8) | ||
|  |             const float s8 = 0.38268343236509f; // sin(pi/8) | ||
|  |             const float g = 5.82842712474619f; // 3 + 2 * sqrt(2) | ||
|  | 
 | ||
|  |             var ch = 2f * (pq.x - pq.y); // approx cos(a/2) | ||
|  |             var sh = pq.z; // approx sin(a/2) | ||
|  |             var r = math.select(math.float4(s8, s8, s8, c8), math.float4(sh, sh, sh, ch), g * sh * sh < ch * ch) * mask; | ||
|  |             return math.normalize(r); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static quaternion qrGivensQuat(float2 pq, float4 mask) | ||
|  |         { | ||
|  |             var l = math.sqrt(pq.x * pq.x + pq.y * pq.y); | ||
|  |             var sh = math.select(0f, pq.y, l > k_EpsilonNormalSqrt); | ||
|  |             var ch = math.abs(pq.x) + math.max(l, k_EpsilonNormalSqrt); | ||
|  |             condSwap(pq.x < 0f, ref sh, ref ch); | ||
|  | 
 | ||
|  |             return math.normalize(math.float4(sh, sh, sh, ch) * mask); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static quaternion givensQRFactorization(float3x3 b, out float3x3 r) | ||
|  |         { | ||
|  |             var u = qrGivensQuat(math.float2(b.c0.x, b.c0.y), math.float4(0f, 0f, 1f, 1f)); | ||
|  |             var qmt = math.float3x3(math.conjugate(u)); | ||
|  |             r = math.mul(qmt, b); | ||
|  | 
 | ||
|  |             var q = qrGivensQuat(math.float2(r.c0.x, r.c0.z), math.float4(0f, -1f, 0f, 1f)); | ||
|  |             u = math.mul(u, q); | ||
|  |             qmt = math.float3x3(math.conjugate(q)); | ||
|  |             r = math.mul(qmt, r); | ||
|  | 
 | ||
|  |             q = qrGivensQuat(math.float2(r.c1.y, r.c1.z), math.float4(1f, 0f, 0f, 1f)); | ||
|  |             u = math.mul(u, q); | ||
|  |             qmt = math.float3x3(math.conjugate(q)); | ||
|  |             r = math.mul(qmt, r); | ||
|  | 
 | ||
|  |             return u; | ||
|  |         } | ||
|  | 
 | ||
|  |         static quaternion jacobiIteration(ref float3x3 s, int iterations = 5) | ||
|  |         { | ||
|  |             float3x3 qm; | ||
|  |             quaternion q; | ||
|  |             quaternion v = quaternion.identity; | ||
|  | 
 | ||
|  |             for (int i = 0; i < iterations; ++i) | ||
|  |             { | ||
|  |                 q = approxGivensQuat(math.float3(s.c0.x, s.c1.y, s.c0.y), math.float4(0f, 0f, 1f, 1f)); | ||
|  |                 v = math.mul(v, q); | ||
|  |                 qm = math.float3x3(q); | ||
|  |                 s = math.mul(math.mul(math.transpose(qm), s), qm); | ||
|  | 
 | ||
|  |                 q = approxGivensQuat(math.float3(s.c1.y, s.c2.z, s.c1.z), math.float4(1f, 0f, 0f, 1f)); | ||
|  |                 v = math.mul(v, q); | ||
|  |                 qm = math.float3x3(q); | ||
|  |                 s = math.mul(math.mul(math.transpose(qm), s), qm); | ||
|  | 
 | ||
|  |                 q = approxGivensQuat(math.float3(s.c2.z, s.c0.x, s.c2.x), math.float4(0f, 1f, 0f, 1f)); | ||
|  |                 v = math.mul(v, q); | ||
|  |                 qm = math.float3x3(q); | ||
|  |                 s = math.mul(math.mul(math.transpose(qm), s), qm); | ||
|  |             } | ||
|  | 
 | ||
|  |             return v; | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static float3 singularValuesDecomposition(float3x3 a, out quaternion u, out quaternion v) | ||
|  |         { | ||
|  |             u = quaternion.identity; | ||
|  |             v = quaternion.identity; | ||
|  | 
 | ||
|  |             var s = math.mul(math.transpose(a), a); | ||
|  |             v = jacobiIteration(ref s); | ||
|  |             var b = math.float3x3(v); | ||
|  |             b = math.mul(a, b); | ||
|  |             sortSingularValues(ref b, ref v); | ||
|  |             u = givensQRFactorization(b, out var e); | ||
|  | 
 | ||
|  |             return math.float3(e.c0.x, e.c1.y, e.c2.z); | ||
|  |         } | ||
|  | 
 | ||
|  |         public const float k_EpsilonDeterminant = 1e-6f; | ||
|  |         public const float k_EpsilonRCP = 1e-9f; | ||
|  |         public const float k_EpsilonNormalSqrt = 1e-15f; | ||
|  |         public const float k_EpsilonNormal = 1e-30f; | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         static float3 rcpsafe(float3 x, float epsilon = k_EpsilonRCP) => | ||
|  |             math.select(math.rcp(x), float3.zero, math.abs(x) < epsilon); | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         public static float3x3 svdInverse(float3x3 a) | ||
|  |         { | ||
|  |             var e = singularValuesDecomposition(a, out var u, out var v); | ||
|  |             var um = math.float3x3(u); | ||
|  |             var vm = math.float3x3(v); | ||
|  | 
 | ||
|  |             return math.mul(vm, math.scaleMul(rcpsafe(e, k_EpsilonDeterminant), math.transpose(um))); | ||
|  |         } | ||
|  | 
 | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         public static quaternion svdRotation(float3x3 a) | ||
|  |         { | ||
|  |             singularValuesDecomposition(a, out var u, out var v); | ||
|  |             return math.mul(u, math.conjugate(v)); | ||
|  |         } | ||
|  |     } | ||
|  | } |