A game about forced loneliness, made by TACStudios
1using System.Runtime.CompilerServices;
2using Unity.IL2CPP.CompilerServices;
3
4namespace Unity.Mathematics
5{
6 // SVD algorithm as described in:
7 // Computing the singular value decomposition of 3x3 matrices with minimal branching and elementary floating point operations,
8 // A.McAdams, A.Selle, R.Tamstorf, J.Teran and E.Sifakis, University of Wisconsin - Madison technical report TR1690, May 2011
9 [Il2CppEagerStaticClassConstruction]
10 static public class svd
11 {
12 [MethodImpl(MethodImplOptions.AggressiveInlining)]
13 static void condSwap(bool c, ref float x, ref float y)
14 {
15 var tmp = x;
16 x = math.select(x, y, c);
17 y = math.select(y, tmp, c);
18 }
19
20 [MethodImpl(MethodImplOptions.AggressiveInlining)]
21 static void condNegSwap(bool c, ref float3 x, ref float3 y)
22 {
23 var tmp = -x;
24 x = math.select(x, y, c);
25 y = math.select(y, tmp, c);
26 }
27
28 [MethodImpl(MethodImplOptions.AggressiveInlining)]
29 static quaternion condNegSwapQuat(bool c, quaternion q, float4 mask)
30 {
31 const float halfSqrt2 = 0.707106781186548f;
32 return math.mul(q, math.select(quaternion.identity.value, mask * halfSqrt2, c));
33 }
34
35 [MethodImpl(MethodImplOptions.AggressiveInlining)]
36 static void sortSingularValues(ref float3x3 b, ref quaternion v)
37 {
38 var l0 = math.lengthsq(b.c0);
39 var l1 = math.lengthsq(b.c1);
40 var l2 = math.lengthsq(b.c2);
41
42 var c = l0 < l1;
43 condNegSwap(c, ref b.c0, ref b.c1);
44 v = condNegSwapQuat(c, v, math.float4(0f, 0f, 1f, 1f));
45 condSwap(c, ref l0, ref l1);
46
47 c = l0 < l2;
48 condNegSwap(c, ref b.c0, ref b.c2);
49 v = condNegSwapQuat(c, v, math.float4(0f, -1f, 0f, 1f));
50 condSwap(c, ref l0, ref l2);
51
52 c = l1 < l2;
53 condNegSwap(c, ref b.c1, ref b.c2);
54 v = condNegSwapQuat(c, v, math.float4(1f, 0f, 0f, 1f));
55 }
56
57 [MethodImpl(MethodImplOptions.AggressiveInlining)]
58 static quaternion approxGivensQuat(float3 pq, float4 mask)
59 {
60 const float c8 = 0.923879532511287f; // cos(pi/8)
61 const float s8 = 0.38268343236509f; // sin(pi/8)
62 const float g = 5.82842712474619f; // 3 + 2 * sqrt(2)
63
64 var ch = 2f * (pq.x - pq.y); // approx cos(a/2)
65 var sh = pq.z; // approx sin(a/2)
66 var r = math.select(math.float4(s8, s8, s8, c8), math.float4(sh, sh, sh, ch), g * sh * sh < ch * ch) * mask;
67 return math.normalize(r);
68 }
69
70 [MethodImpl(MethodImplOptions.AggressiveInlining)]
71 static quaternion qrGivensQuat(float2 pq, float4 mask)
72 {
73 var l = math.sqrt(pq.x * pq.x + pq.y * pq.y);
74 var sh = math.select(0f, pq.y, l > k_EpsilonNormalSqrt);
75 var ch = math.abs(pq.x) + math.max(l, k_EpsilonNormalSqrt);
76 condSwap(pq.x < 0f, ref sh, ref ch);
77
78 return math.normalize(math.float4(sh, sh, sh, ch) * mask);
79 }
80
81 [MethodImpl(MethodImplOptions.AggressiveInlining)]
82 static quaternion givensQRFactorization(float3x3 b, out float3x3 r)
83 {
84 var u = qrGivensQuat(math.float2(b.c0.x, b.c0.y), math.float4(0f, 0f, 1f, 1f));
85 var qmt = math.float3x3(math.conjugate(u));
86 r = math.mul(qmt, b);
87
88 var q = qrGivensQuat(math.float2(r.c0.x, r.c0.z), math.float4(0f, -1f, 0f, 1f));
89 u = math.mul(u, q);
90 qmt = math.float3x3(math.conjugate(q));
91 r = math.mul(qmt, r);
92
93 q = qrGivensQuat(math.float2(r.c1.y, r.c1.z), math.float4(1f, 0f, 0f, 1f));
94 u = math.mul(u, q);
95 qmt = math.float3x3(math.conjugate(q));
96 r = math.mul(qmt, r);
97
98 return u;
99 }
100
101 static quaternion jacobiIteration(ref float3x3 s, int iterations = 5)
102 {
103 float3x3 qm;
104 quaternion q;
105 quaternion v = quaternion.identity;
106
107 for (int i = 0; i < iterations; ++i)
108 {
109 q = approxGivensQuat(math.float3(s.c0.x, s.c1.y, s.c0.y), math.float4(0f, 0f, 1f, 1f));
110 v = math.mul(v, q);
111 qm = math.float3x3(q);
112 s = math.mul(math.mul(math.transpose(qm), s), qm);
113
114 q = approxGivensQuat(math.float3(s.c1.y, s.c2.z, s.c1.z), math.float4(1f, 0f, 0f, 1f));
115 v = math.mul(v, q);
116 qm = math.float3x3(q);
117 s = math.mul(math.mul(math.transpose(qm), s), qm);
118
119 q = approxGivensQuat(math.float3(s.c2.z, s.c0.x, s.c2.x), math.float4(0f, 1f, 0f, 1f));
120 v = math.mul(v, q);
121 qm = math.float3x3(q);
122 s = math.mul(math.mul(math.transpose(qm), s), qm);
123 }
124
125 return v;
126 }
127
128 [MethodImpl(MethodImplOptions.AggressiveInlining)]
129 static float3 singularValuesDecomposition(float3x3 a, out quaternion u, out quaternion v)
130 {
131 u = quaternion.identity;
132 v = quaternion.identity;
133
134 var s = math.mul(math.transpose(a), a);
135 v = jacobiIteration(ref s);
136 var b = math.float3x3(v);
137 b = math.mul(a, b);
138 sortSingularValues(ref b, ref v);
139 u = givensQRFactorization(b, out var e);
140
141 return math.float3(e.c0.x, e.c1.y, e.c2.z);
142 }
143
144 public const float k_EpsilonDeterminant = 1e-6f;
145 public const float k_EpsilonRCP = 1e-9f;
146 public const float k_EpsilonNormalSqrt = 1e-15f;
147 public const float k_EpsilonNormal = 1e-30f;
148
149 [MethodImpl(MethodImplOptions.AggressiveInlining)]
150 static float3 rcpsafe(float3 x, float epsilon = k_EpsilonRCP) =>
151 math.select(math.rcp(x), float3.zero, math.abs(x) < epsilon);
152
153 [MethodImpl(MethodImplOptions.AggressiveInlining)]
154 public static float3x3 svdInverse(float3x3 a)
155 {
156 var e = singularValuesDecomposition(a, out var u, out var v);
157 var um = math.float3x3(u);
158 var vm = math.float3x3(v);
159
160 return math.mul(vm, math.scaleMul(rcpsafe(e, k_EpsilonDeterminant), math.transpose(um)));
161 }
162
163 [MethodImpl(MethodImplOptions.AggressiveInlining)]
164 public static quaternion svdRotation(float3x3 a)
165 {
166 singularValuesDecomposition(a, out var u, out var v);
167 return math.mul(u, math.conjugate(v));
168 }
169 }
170}