nlib
SimdQuaternion.h
Go to the documentation of this file.
1 
2 /*--------------------------------------------------------------------------------*
3  Project: CrossRoad
4  Copyright (C)Nintendo All rights reserved.
5 
6  These coded instructions, statements, and computer programs contain proprietary
7  information of Nintendo and/or its licensed developers and are protected by
8  national and international copyright laws. They may not be disclosed to third
9  parties or copied or duplicated in any form, in whole or in part, without the
10  prior written consent of Nintendo.
11 
12  The content herein is highly confidential and should be handled accordingly.
13  *--------------------------------------------------------------------------------*/
14 
15 #pragma once
16 #ifndef INCLUDE_NN_NLIB_SIMD_SIMDQUATERNION_H_
17 #define INCLUDE_NN_NLIB_SIMD_SIMDQUATERNION_H_
18 
19 #include "nn/nlib/simd/SimdFloat.h"
21 
22 NLIB_NAMESPACE_BEGIN
23 namespace simd {
24 
26  public:
27  static SimdQuaternion __vectorcall Identity() NLIB_NOEXCEPT;
28  static SimdQuaternion __vectorcall Conjugate(SimdQuaternionArg q) NLIB_NOEXCEPT;
29  static f128 __vectorcall Length(SimdQuaternionArg q) NLIB_NOEXCEPT;
30  static f128 __vectorcall LengthSq(SimdQuaternionArg q) NLIB_NOEXCEPT;
31  static f128 __vectorcall RecpLength(SimdQuaternionArg q) NLIB_NOEXCEPT;
32  static SimdQuaternion __vectorcall Normalize(SimdQuaternionArg q) NLIB_NOEXCEPT;
33  static SimdQuaternion __vectorcall NormalizeEst(SimdQuaternionArg q) NLIB_NOEXCEPT;
34  static SimdQuaternion __vectorcall Inverse(SimdQuaternionArg q) NLIB_NOEXCEPT;
35  static SimdQuaternion __vectorcall Ln(SimdQuaternionArg q_normalized) NLIB_NOEXCEPT;
36  static SimdQuaternion __vectorcall Exp(SimdQuaternionArg q) NLIB_NOEXCEPT;
37 
38  static bool __vectorcall IsIdentity(SimdQuaternionArg q) NLIB_NOEXCEPT;
39  static bool __vectorcall IsInfinite(SimdQuaternionArg q) NLIB_NOEXCEPT;
40  static bool __vectorcall IsNaN(SimdQuaternionArg q) NLIB_NOEXCEPT;
41  static bool __vectorcall CmpEq(SimdQuaternionArg q0, SimdQuaternionArg q1) NLIB_NOEXCEPT;
42  static bool __vectorcall CmpNe(SimdQuaternionArg q0, SimdQuaternionArg q1) NLIB_NOEXCEPT;
43 
44  static f128 __vectorcall Dot(SimdQuaternionArg q0, SimdQuaternionArg q1) NLIB_NOEXCEPT;
45  static SimdQuaternion __vectorcall
47 
48  static SimdQuaternion __vectorcall
49  FromRotationAxisAndSinCos(SimdVectorArg axis_normalized, float sin_half_rad,
50  float cos_half_rad) NLIB_NOEXCEPT;
51  static SimdQuaternion __vectorcall FromRotationMatrix(SimdMatrixArg m) NLIB_NOEXCEPT;
52  static SimdQuaternion __vectorcall
53  FromRotationZXY(SimdVectorArg sin_half_xyz, SimdVectorArg cos_half_xyz) NLIB_NOEXCEPT;
54  static SimdVector __vectorcall ToAxisAngle(float* rad, SimdQuaternion q) NLIB_NOEXCEPT;
55 
56  static SimdQuaternion __vectorcall Slerp(SimdQuaternionArg q0_normalized,
57  SimdQuaternionArg q1_normalized,
58  float t) NLIB_NOEXCEPT;
59  static SimdQuaternion __vectorcall
60  Squad(SimdQuaternionArg q0_normalized, SimdQuaternionArg q1_normalized,
61  SimdQuaternionArg q2_normalized, SimdQuaternionArg q3_normalized,
62  float t) NLIB_NOEXCEPT;
63  static void __vectorcall SquadSetup(SimdQuaternion* a, SimdQuaternion* b, SimdQuaternion* c,
66  static SimdQuaternion __vectorcall BaryCentric(SimdQuaternionArg q0, SimdQuaternionArg q1,
67  SimdQuaternionArg q2, float f,
68  float g) NLIB_NOEXCEPT;
69 
70  private:
71  Quaternion(); // forbidden
72 };
73 
74 #ifndef NLIB_DOXYGEN
75 
76 #define NLIB_M(tp) inline tp __vectorcall
77 
78 // XMQuaternionIdentity
79 NLIB_M(SimdQuaternion) Quaternion::Identity() NLIB_NOEXCEPT { return F128::Set0001(); }
80 
81 // XMQuaternionConjugate
82 NLIB_M(SimdQuaternion) Quaternion::Conjugate(SimdQuaternionArg q) NLIB_NOEXCEPT {
83  return F128::NegateEx<true, true, true, false>(q);
84 }
85 
86 // XMQuaternionLength
87 NLIB_M(f128) Quaternion::Length(SimdQuaternionArg q) NLIB_NOEXCEPT { return Vector4::Length(q); }
88 
89 // XMQuaternionLengthSq
90 NLIB_M(f128) Quaternion::LengthSq(SimdQuaternionArg q) NLIB_NOEXCEPT {
91  return Vector4::LengthSq(q);
92 }
93 
94 // XMQuaternionReciprocalLength
95 NLIB_M(f128) Quaternion::RecpLength(SimdQuaternionArg q) NLIB_NOEXCEPT {
96  return Vector4::RecpLength(q);
97 }
98 
99 // XMQuaternionNormalize
100 NLIB_M(SimdQuaternion) Quaternion::Normalize(SimdQuaternionArg q) NLIB_NOEXCEPT {
101  return Vector4::Normalize(q);
102 }
103 
104 // XMQuaternionNormalizeEst
105 NLIB_M(SimdQuaternion) Quaternion::NormalizeEst(SimdQuaternionArg q) NLIB_NOEXCEPT {
106  return Vector4::NormalizeEst(q);
107 }
108 
109 // XMQuaternionInverse
110 NLIB_M(SimdQuaternion) Quaternion::Inverse(SimdQuaternionArg q) NLIB_NOEXCEPT {
111  f128 len_sq = LengthSq(q);
112  SimdQuaternion conj = Conjugate(q);
113 
114  f128 eps = F128::SetEpsilon();
115  f128 near_zero = F128::CmpLe(len_sq, eps);
116 
117  SimdQuaternion inv = F128::Div(conj, len_sq);
118  return F128::AndNot(near_zero, inv);
119 }
120 
121 // XMQuaternionLn
122 NLIB_M(SimdQuaternion) Quaternion::Ln(SimdQuaternionArg q_normalized) NLIB_NOEXCEPT {
123  static const float one_eps = 1.f - 0.00001f;
124  f128 q0 = F128::SetZeroToLane<3>(q_normalized);
125  f128 w = F128::SetValue<3>(q_normalized, each_select32);
126  f128 xyz_len = Vector4::Length(q0);
127  f128 w_not_near_one = F128::InBound(w, F128::SetValue(one_eps, each_float));
128 
129  f128 theta = F128::ArcTan2(xyz_len, w);
130  f128 result = F128::Div(theta, xyz_len);
131  result = F128::Mult(q0, result);
132  result = F128::Select(w_not_near_one, result, q0);
133  return result;
134 }
135 
136 // XMQuaternionExp
137 NLIB_M(SimdQuaternion) Quaternion::Exp(SimdQuaternionArg q) NLIB_NOEXCEPT {
138  f128 xyz_len = Vector4::Length(F128::SetZeroToLane<3>(q));
139  f128x2 sc = F128::SinCos(xyz_len);
140 
141  f128 result = F128::Mult(q, F128::Div(sc.val[0], xyz_len));
142  f128 near_zero = F128::CmpNearEqZero(xyz_len, F128::SetEpsilon());
143  result = F128::Select(near_zero, q, result);
144  result = F128::Splat<false, false, false, true>(result, sc.val[1]);
145  return result;
146 }
147 
148 // XMQuaternionIsIdentity
149 NLIB_M(bool) Quaternion::IsIdentity(SimdQuaternionArg q) NLIB_NOEXCEPT { // NOLINT
150  return Vector4::CmpEq(q, Identity());
151 }
152 
153 // XMQuaternionIsInfinite
154 NLIB_M(bool) Quaternion::IsInfinite(SimdQuaternionArg q) NLIB_NOEXCEPT { // NOLINT
155  return Vector4::IsInfinite(q);
156 }
157 
158 // XMQuaternionIsNaN
159 NLIB_M(bool) Quaternion::IsNaN(SimdQuaternionArg q) NLIB_NOEXCEPT { // NOLINT
160  return Vector4::IsNaN(q);
161 }
162 
163 // XMQuaternionEqual
164 NLIB_M(bool) Quaternion::CmpEq(SimdQuaternionArg q0, // NOLINT
166  return Vector4::CmpEq(q0, q1);
167 }
168 
169 // XMQuaternionNotEqual
170 NLIB_M(bool) Quaternion::CmpNe(SimdQuaternionArg q0, // NOLINT
172  return Vector4::CmpNe(q0, q1);
173 }
174 
175 // XMQuaternionDot
176 NLIB_M(f128) Quaternion::Dot(SimdQuaternionArg q0, SimdQuaternionArg q1) NLIB_NOEXCEPT {
177  return Vector4::Dot(q0, q1);
178 }
179 
180 // XMQuaternionMultiply
181 NLIB_M(SimdQuaternion) Quaternion::Mult(SimdQuaternionArg q0, SimdQuaternionArg q1) NLIB_NOEXCEPT {
182  SimdVector v1 = F128::Swizzle<3, 0, 1, 2>(q1);
183  f128 r1 = F128::Swizzle<3, 2, 1, 0>(q0);
184  f128 r2 = F128::Swizzle<2, 3, 0, 1>(q0);
185  f128 r3 = F128::Swizzle<1, 0, 3, 2>(q0);
186  SimdMatrix m;
187  m.r[0] = q0;
188  m.r[1] = F128::NegateEx<false, true, false, true>(r1);
189  m.r[2] = F128::NegateEx<false, false, true, true>(r2);
190  m.r[3] = F128::NegateEx<true, false, false, true>(r3);
191  return Vector4::Transform(v1, m);
192 }
193 
194 // sin_half_rad = sin(rad/2), cos_half_rad = cos(rad/2)
195 // XMQuaternionRotationNormal
196 NLIB_M(SimdQuaternion) Quaternion::FromRotationAxisAndSinCos(SimdVectorArg axis_normalized,
197  float sin_half_rad,
198  float cos_half_rad) NLIB_NOEXCEPT {
199  SimdVector axis = F128::SetFloatToLane<3>(axis_normalized, 1.f);
200  f128 scale = F128::SetValue(sin_half_rad, sin_half_rad, sin_half_rad, cos_half_rad);
201  return F128::Mult(axis, scale);
202 }
203 
204 // m must be rotation matrix
205 NLIB_M(SimdQuaternion) Quaternion::FromRotationMatrix(SimdMatrixArg m) NLIB_NOEXCEPT {
206  // 1-2y^2-2z^2 2xy+2wz 2xz-2wy
207  // 2xy-2wz 1-2x^2-2z^2 2yz+2wx
208  // 2xz+2wy 2yz-2wx 1^2x^2^2y^2
209  f128 r0 = m.r[0];
210  f128 r1 = m.r[1];
211  f128 r2 = m.r[2];
212 
213  f128 elem;
214  // elem = { m00 - m11 - m22 + 1.f, -m00 + m11 - m22 + 1.f,
215  // -m00 - m11 + m22 + 1.f, m00 + m11 + m22 + 1.f }
216  // -> { 4x^2, 4y^2, 4z^2, 4w^2 }
217  {
218  f128 m00x = F128::SetValue<0>(r0, each_select32);
219  f128 m11x = F128::SetValue<1>(r1, each_select32);
220  f128 m22x = F128::SetValue<2>(r2, each_select32);
221  m00x = F128::NegateEx<false, true, true, false>(m00x);
222  m11x = F128::NegateEx<true, false, true, false>(m11x);
223  m22x = F128::NegateEx<true, true, false, false>(m22x);
224  f128 one = F128::SetOne();
225  elem = F128::Add(m00x, m11x);
226  elem = F128::Add(elem, m22x);
227  elem = F128::Add(elem, one);
228  }
229 
230  f128 xx_ge_yy;
231  f128 zz_ge_ww;
232  f128 xxyy_ge_zzww;
233  f128 elem_max;
234  {
235  f128 t0, t1, t2, t3;
236  t0 = F128::SetValue<0>(elem, each_select32);
237  t1 = F128::SetValue<1>(elem, each_select32);
238  t2 = F128::SetValue<2>(elem, each_select32);
239  t3 = F128::SetValue<3>(elem, each_select32);
240  xx_ge_yy = F128::CmpGe(t0, t1);
241  zz_ge_ww = F128::CmpGe(t2, t3);
242 
243  t0 = F128::PairwiseMax(elem, elem);
244  t2 = F128::SetValue<0>(t0, each_select32);
245  t3 = F128::SetValue<1>(t0, each_select32);
246  elem_max = F128::PairwiseMax(t0, t0);
247  xxyy_ge_zzww = F128::CmpGe(t2, t3);
248  }
249 
250  f128 v0_25 = F128::SetValue(0.25f, each_float);
251  elem_max = F128::Mult(v0_25, elem_max);
252  f128 mult = F128::RecpSqrt(elem_max);
253  f128 v = F128::Mult(mult, elem_max);
254  mult = F128::Mult(v0_25, mult);
255 
256  f128 m01_20_12;
257  m01_20_12 = F128::Permute<1, 4, 2, -1>(r0, r2);
258  m01_20_12 = F128::Permute<0, 1, 6, -1>(m01_20_12, r1);
259 
260  f128 m10_02_21;
261  m10_02_21 = F128::Permute<0, 1, 5, -1>(r1, r2);
262  m10_02_21 = F128::Permute<0, 6, 2, -1>(m10_02_21, r0);
263 
264  f128 ans_x_biggest, ans_y_biggest, ans_z_biggest, ans_w_biggest;
265  {
266  f128 tmp_x, tmp_y, tmp_z, tmp_w;
267  tmp_x = F128::NegateEx<false, false, true, true>(m10_02_21);
268  tmp_x = F128::Mult(mult, F128::Add(m01_20_12, tmp_x));
269 
270  tmp_y = F128::NegateEx<false, true, false, true>(m10_02_21);
271  tmp_y = F128::Mult(mult, F128::Add(m01_20_12, tmp_y));
272 
273  tmp_z = F128::NegateEx<true, false, false, true>(m10_02_21);
274  tmp_z = F128::Mult(mult, F128::Add(m01_20_12, tmp_z));
275 
276  tmp_w = F128::Mult(mult, F128::Sub(m01_20_12, m10_02_21));
277 
278  ans_x_biggest = F128::Permute<4, 0, 1, 2>(tmp_x, v);
279  ans_y_biggest = F128::Permute<0, 4, 2, 1>(tmp_y, v);
280  ans_z_biggest = F128::Permute<1, 2, 4, 0>(tmp_z, v);
281  ans_w_biggest = F128::Permute<2, 1, 0, 4>(tmp_w, v);
282  }
283 
284  f128 ans_xy = F128::Select(xx_ge_yy, ans_x_biggest, ans_y_biggest);
285  f128 ans_zw = F128::Select(zz_ge_ww, ans_z_biggest, ans_w_biggest);
286  return F128::Select(xxyy_ge_zzww, ans_xy, ans_zw);
287 }
288 
289 // sin_half_xyz = { sin(rad_x/2), sin(rad_y/2), sin(rad_z/2), * }
290 // cos_half_xyz = { cos(rad_x/2), cos(rad_y/2), cos(rad_z/2), * }
291 // XMQuaternionRotationRollPitchYawFromVector
292 NLIB_M(SimdQuaternion) Quaternion::FromRotationZXY(SimdVectorArg sin_half_xyz,
293  SimdVectorArg cos_half_xyz) NLIB_NOEXCEPT {
294  // CxSySz + SxCyCz
295  // -SxCySz + CxSyCz
296  // -SxSyCz + CxCySz
297  // SxSySz + CxCyCz
298  f128 x1 = F128::Permute<4, 0, 0, 0>(sin_half_xyz, cos_half_xyz);
299  f128 y1 = F128::Permute<1, 5, 1, 1>(sin_half_xyz, cos_half_xyz);
300  f128 z1 = F128::Permute<2, 2, 6, 2>(sin_half_xyz, cos_half_xyz);
301  x1 = F128::NegateEx<false, true, true, false>(x1);
302  f128 x0 = F128::Permute<0, 4, 4, 4>(sin_half_xyz, cos_half_xyz);
303  f128 y0 = F128::Permute<5, 1, 5, 5>(sin_half_xyz, cos_half_xyz);
304  f128 z0 = F128::Permute<6, 6, 2, 6>(sin_half_xyz, cos_half_xyz);
305 
306  f128 z0x0y0 = F128::Mult(x0, y0);
307  f128 z1x1y1 = F128::Mult(x1, y1);
308  z0x0y0 = F128::Mult(z0x0y0, z0);
309  return F128::MultAdd(z1x1y1, z1, z0x0y0);
310 }
311 
312 // XMQuaternionToAxisAngle
313 // rad can be nullptr
314 NLIB_M(SimdVector) Quaternion::ToAxisAngle(float* rad, SimdQuaternion q) NLIB_NOEXCEPT {
315  if (rad) {
316  *rad = 2.f * F128::GetFloatFromLane<3>(F128::ArcCos(q));
317  }
318  return q;
319 }
320 
321 // XMQuaternionSlerp
322 NLIB_M(SimdVector) Quaternion::Slerp(SimdQuaternionArg q0_normalized,
323  SimdQuaternionArg q1_normalized, float t) NLIB_NOEXCEPT {
324  f128 q0q1 = Dot(q0_normalized, q1_normalized);
325  f128 too_near;
326  f128 sp;
327  {
328  f128 ss = F128::MultSub(q0q1, q0q1, F128::SetValue(1.f, each_float));
329  f128 eps = F128::SetEpsilon();
330  too_near = F128::CmpLe(ss, eps);
331  sp = F128::RecpSqrt(ss);
332  }
333  f128 ph = F128::ArcCos(q0q1);
334  f128 k = F128::SetValue(1.f - t, t, 0.f, 0.f);
335  f128 t0t1 = F128::Mult(sp, F128::Sin(F128::Mult(ph, k)));
336  f128 t0 = F128::SetValue<0>(t0t1, each_select32);
337  f128 t1 = F128::SetValue<1>(t0t1, each_select32);
338 
339  SimdVector ret = F128::Mult(q0_normalized, t0);
340  ret = F128::MultAdd(q1_normalized, t1, ret);
341  return F128::Select(too_near, q0_normalized, ret);
342 }
343 
344 // XMQuaternionSquad
345 NLIB_M(SimdVector) Quaternion::Squad(SimdQuaternionArg q0_normalized,
346  SimdQuaternionArg q1_normalized,
347  SimdQuaternionArg q2_normalized,
348  SimdQuaternionArg q3_normalized, float t) NLIB_NOEXCEPT {
349  float t2 = (t - t * t) * 2.f;
350  SimdQuaternion q03 = Slerp(q0_normalized, q3_normalized, t);
351  SimdQuaternion q12 = Slerp(q1_normalized, q2_normalized, t);
352  return Slerp(q03, q12, t2);
353 }
354 
355 // XMQuaternionBaryCentric
356 NLIB_M(SimdVector) Quaternion::BaryCentric(SimdQuaternionArg q0, SimdQuaternionArg q1,
357  SimdQuaternionArg q2, float f, float g) NLIB_NOEXCEPT {
358  float fg = f + g;
359  if (fg <= 0.00001f && fg >= -0.00001f) return q0; // to avoid division by zero
360  SimdQuaternion q01 = Slerp(q0, q1, fg);
361  SimdQuaternion q02 = Slerp(q0, q2, fg);
362  return Slerp(q01, q02, g / fg);
363 }
364 
365 // XMQuaternionSquadSetup
366 inline void __vectorcall Quaternion::SquadSetup(SimdQuaternion* a, SimdQuaternion* b,
367  SimdQuaternion* c,
371  SimdQuaternion Q0, Q2, Q3;
372  {
373  f128 lensq_a01 = Quaternion::LengthSq(F128::Add(q0, q1));
374  f128 lensq_s01 = Quaternion::LengthSq(F128::Sub(q0, q1));
375  f128 cmp01 = F128::CmpLt(lensq_a01, lensq_s01);
376  f128 neg_q0 = F128::Negate(q0);
377  Q0 = F128::Select(cmp01, neg_q0, q0);
378 
379  f128 lensq_a12 = Quaternion::LengthSq(F128::Add(q1, q2));
380  f128 lensq_s12 = Quaternion::LengthSq(F128::Sub(q1, q2));
381  f128 cmp12 = F128::CmpLt(lensq_a12, lensq_s12);
382  f128 neg_q2 = F128::Negate(q2);
383  Q2 = F128::Select(cmp12, neg_q2, q2);
384 
385  f128 lensq_a23 = Quaternion::LengthSq(F128::Add(q2, q3));
386  f128 lensq_s23 = Quaternion::LengthSq(F128::Sub(q2, q3));
387  f128 cmp23 = F128::CmpLt(lensq_a23, lensq_s23);
388  f128 neg_q3 = F128::Negate(q3);
389  Q3 = F128::Select(cmp23, neg_q3, q3);
390  }
391  const SimdQuaternion& Q1 = q1;
392 
393  SimdQuaternion InvQ1 = Quaternion::Inverse(Q1);
394  SimdQuaternion Ln_ExpQ1_Q2 = Quaternion::Ln(Quaternion::Mult(InvQ1, Q2));
395  SimdQuaternion Ln_ExpQ1_Q0 = Quaternion::Ln(Quaternion::Mult(InvQ1, Q0));
396 
397  SimdQuaternion InvQ2 = Quaternion::Inverse(Q2);
398  SimdQuaternion Ln_ExpQ2_Q3 = Quaternion::Ln(Quaternion::Mult(InvQ2, Q3));
399  SimdQuaternion Ln_ExpQ2_Q1 = Quaternion::Ln(Quaternion::Mult(InvQ2, Q1));
400 
401  f128 v0_25 = F128::SetValue(0.25f, each_float);
402  SimdQuaternion A = F128::Mult(v0_25, F128::Add(Ln_ExpQ1_Q2, Ln_ExpQ1_Q0));
403  SimdQuaternion B = F128::Mult(v0_25, F128::Add(Ln_ExpQ2_Q3, Ln_ExpQ2_Q1));
404  A = Quaternion::Exp(A);
405  B = Quaternion::Exp(B);
406 
407  *a = Quaternion::Mult(Q1, A);
408  *b = Quaternion::Mult(Q2, B);
409  *c = Q2;
410 }
411 
412 #undef NLIB_M
413 
414 #endif // NLIB_DOXYGEN
415 
416 } // namespace simd
417 NLIB_NAMESPACE_END
418 
419 #endif // INCLUDE_NN_NLIB_SIMD_SIMDQUATERNION_H_
The class with the collection of functions that handle quaternions.
f128arg SimdVectorArg
f128arg is defined using typedef.
Definition: SimdFloat.h:4148
The type for two SIMD registers for 128-bit, single-precision, floating-point numbers.
Definition: SimdFloat.h:48
#define NLIB_VIS_HIDDEN
Symbols for functions and classes are not made available outside of the library.
Definition: Platform_unix.h:88
constexpr const each_float_tag each_float
The tag for representing a single-precision floating-point number with an each_float_tag-type constan...
Definition: SimdFloat.h:68
f128arg SimdQuaternionArg
f128arg is defined using typedef.
Definition: SimdFloat.h:4150
f128 r[4]
Keeps each row of a 4x4 matrix.
Definition: SimdFloat.h:4175
The structure for keeping a 4x4 matrix.
Definition: SimdFloat.h:4159
#define NLIB_NOEXCEPT
Defines noexcept geared to the environment, or the equivalent.
Definition: Config.h:105
Defines the class and functions for SIMD computations on single-precision floating-point numbers...
constexpr const each_select32_tag each_select32
The tag for representing the selection of a 32-bit lane with an each_select32_tag-type constant objec...
Definition: SimdInt.h:63
nlib_f128_t f128
nlib_f128_t is defined using typedef.
Definition: SimdFloat.h:71
Defines a four-dimensional vector.
f128 SimdQuaternion
f128 is defined using typedef. Used when handling quaternions.
Definition: SimdFloat.h:4149
f128 SimdVector
f128 is defined using typedef. Used when handling three-dimensional or four-dimensional vectors...
Definition: SimdFloat.h:4147