Blender V4.3
math_matrix.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_matrix.hh"
10
11#include "BLI_math_rotation.hh"
12#include "BLI_simd.hh"
13
14#include <Eigen/Core>
15#include <Eigen/Dense>
16#include <Eigen/Eigenvalues>
17
18/* -------------------------------------------------------------------- */
22namespace blender {
23
24template<> float4x4 operator*(const float4x4 &a, const float4x4 &b)
25{
26 using namespace math;
28
29#if BLI_HAVE_SSE2
30 __m128 A0 = _mm_load_ps(a[0]);
31 __m128 A1 = _mm_load_ps(a[1]);
32 __m128 A2 = _mm_load_ps(a[2]);
33 __m128 A3 = _mm_load_ps(a[3]);
34
35 for (int i = 0; i < 4; i++) {
36 __m128 B0 = _mm_set1_ps(b[i][0]);
37 __m128 B1 = _mm_set1_ps(b[i][1]);
38 __m128 B2 = _mm_set1_ps(b[i][2]);
39 __m128 B3 = _mm_set1_ps(b[i][3]);
40
41 __m128 sum = _mm_add_ps(_mm_add_ps(_mm_mul_ps(B0, A0), _mm_mul_ps(B1, A1)),
42 _mm_add_ps(_mm_mul_ps(B2, A2), _mm_mul_ps(B3, A3)));
43
44 _mm_store_ps(result[i], sum);
45 }
46#else
47 result[0][0] = b[0][0] * a[0][0] + b[0][1] * a[1][0] + b[0][2] * a[2][0] + b[0][3] * a[3][0];
48 result[0][1] = b[0][0] * a[0][1] + b[0][1] * a[1][1] + b[0][2] * a[2][1] + b[0][3] * a[3][1];
49 result[0][2] = b[0][0] * a[0][2] + b[0][1] * a[1][2] + b[0][2] * a[2][2] + b[0][3] * a[3][2];
50 result[0][3] = b[0][0] * a[0][3] + b[0][1] * a[1][3] + b[0][2] * a[2][3] + b[0][3] * a[3][3];
51
52 result[1][0] = b[1][0] * a[0][0] + b[1][1] * a[1][0] + b[1][2] * a[2][0] + b[1][3] * a[3][0];
53 result[1][1] = b[1][0] * a[0][1] + b[1][1] * a[1][1] + b[1][2] * a[2][1] + b[1][3] * a[3][1];
54 result[1][2] = b[1][0] * a[0][2] + b[1][1] * a[1][2] + b[1][2] * a[2][2] + b[1][3] * a[3][2];
55 result[1][3] = b[1][0] * a[0][3] + b[1][1] * a[1][3] + b[1][2] * a[2][3] + b[1][3] * a[3][3];
56
57 result[2][0] = b[2][0] * a[0][0] + b[2][1] * a[1][0] + b[2][2] * a[2][0] + b[2][3] * a[3][0];
58 result[2][1] = b[2][0] * a[0][1] + b[2][1] * a[1][1] + b[2][2] * a[2][1] + b[2][3] * a[3][1];
59 result[2][2] = b[2][0] * a[0][2] + b[2][1] * a[1][2] + b[2][2] * a[2][2] + b[2][3] * a[3][2];
60 result[2][3] = b[2][0] * a[0][3] + b[2][1] * a[1][3] + b[2][2] * a[2][3] + b[2][3] * a[3][3];
61
62 result[3][0] = b[3][0] * a[0][0] + b[3][1] * a[1][0] + b[3][2] * a[2][0] + b[3][3] * a[3][0];
63 result[3][1] = b[3][0] * a[0][1] + b[3][1] * a[1][1] + b[3][2] * a[2][1] + b[3][3] * a[3][1];
64 result[3][2] = b[3][0] * a[0][2] + b[3][1] * a[1][2] + b[3][2] * a[2][2] + b[3][3] * a[3][2];
65 result[3][3] = b[3][0] * a[0][3] + b[3][1] * a[1][3] + b[3][2] * a[2][3] + b[3][3] * a[3][3];
66#endif
67
68 return result;
69}
70
71template<> float3x3 operator*(const float3x3 &a, const float3x3 &b)
72{
73 using namespace math;
75
76#if 0 /* 1.2 times slower. Could be used as reference for aligned version. */
77 __m128 A0 = _mm_set_ps(0, a[0][2], a[0][1], a[0][0]);
78 __m128 A1 = _mm_set_ps(0, a[1][2], a[1][1], a[1][0]);
79 __m128 A2 = _mm_set_ps(0, a[2][2], a[2][1], a[2][0]);
80
81 for (int i = 0; i < 2; i++) {
82 __m128 B0 = _mm_set1_ps(b[i][0]);
83 __m128 B1 = _mm_set1_ps(b[i][1]);
84 __m128 B2 = _mm_set1_ps(b[i][2]);
85 __m128 sum = _mm_add_ps(_mm_add_ps(_mm_mul_ps(B0, A0), _mm_mul_ps(B1, A1)),
86 _mm_mul_ps(B2, A2));
87 _mm_storeu_ps(result[i], sum);
88 }
89
90 _mm_storeu_ps(result[1], sum[1]);
91 /* Manual per component store to avoid segfault. */
92 _mm_store_ss(&result[2][0], sum[2]);
93 sum[2] = _mm_shuffle_ps(sum[2], sum[2], _MM_SHUFFLE(3, 2, 1, 1));
94 _mm_store_ss(&result[2][1], sum[2]);
95 sum[2] = _mm_shuffle_ps(sum[2], sum[2], _MM_SHUFFLE(3, 2, 1, 2));
96 _mm_store_ss(&result[2][2], sum[2]);
97
98#else
100 result[0][0] = b[0][0] * a[0][0] + b[0][1] * a[1][0] + b[0][2] * a[2][0];
101 result[0][1] = b[0][0] * a[0][1] + b[0][1] * a[1][1] + b[0][2] * a[2][1];
102 result[0][2] = b[0][0] * a[0][2] + b[0][1] * a[1][2] + b[0][2] * a[2][2];
103
104 result[1][0] = b[1][0] * a[0][0] + b[1][1] * a[1][0] + b[1][2] * a[2][0];
105 result[1][1] = b[1][0] * a[0][1] + b[1][1] * a[1][1] + b[1][2] * a[2][1];
106 result[1][2] = b[1][0] * a[0][2] + b[1][1] * a[1][2] + b[1][2] * a[2][2];
107
108 result[2][0] = b[2][0] * a[0][0] + b[2][1] * a[1][0] + b[2][2] * a[2][0];
109 result[2][1] = b[2][0] * a[0][1] + b[2][1] * a[1][1] + b[2][2] * a[2][1];
110 result[2][2] = b[2][0] * a[0][2] + b[2][1] * a[1][2] + b[2][2] * a[2][2];
111#endif
112 return result;
113}
114
115template float2x2 operator*(const float2x2 &a, const float2x2 &b);
116template double2x2 operator*(const double2x2 &a, const double2x2 &b);
117template double3x3 operator*(const double3x3 &a, const double3x3 &b);
118template double4x4 operator*(const double4x4 &a, const double4x4 &b);
119
120} // namespace blender
121
124namespace blender::math {
125
126/* -------------------------------------------------------------------- */
130template<typename T, int Size> T determinant(const MatBase<T, Size, Size> &mat)
131{
132 return Eigen::Map<const Eigen::Matrix<T, Size, Size>>(mat.base_ptr()).determinant();
133}
134
135template float determinant(const float2x2 &mat);
136template float determinant(const float3x3 &mat);
137template float determinant(const float4x4 &mat);
138template double determinant(const double2x2 &mat);
139template double determinant(const double3x3 &mat);
140template double determinant(const double4x4 &mat);
141
142template<typename T> bool is_negative(const MatBase<T, 4, 4> &mat)
143{
144 return Eigen::Map<const Eigen::Matrix<T, 3, 3>, 0, Eigen::Stride<4, 1>>(mat.base_ptr())
145 .determinant() < T(0);
146}
147
148template bool is_negative(const float4x4 &mat);
149template bool is_negative(const double4x4 &mat);
150
153/* -------------------------------------------------------------------- */
157template<typename T, int Size> MatBase<T, Size, Size> adjoint(const MatBase<T, Size, Size> &mat)
158{
160 unroll<Size>([&](auto c) {
161 unroll<Size>([&](auto r) {
162 /* Copy other cells except the "cross" to compute the determinant. */
163 MatBase<T, Size - 1, Size - 1> tmp;
164 unroll<Size>([&](auto m_c) {
165 unroll<Size>([&](auto m_r) {
166 if (m_c != c && m_r != r) {
167 int d_c = (m_c < c) ? m_c : (m_c - 1);
168 int d_r = (m_r < r) ? m_r : (m_r - 1);
169 tmp[d_c][d_r] = mat[m_c][m_r];
170 }
171 });
172 });
173 T minor = determinant(tmp);
174 /* Transpose directly to get the adjugate. Swap destination row and col. */
175 adj[r][c] = ((c + r) & 1) ? -minor : minor;
176 });
177 });
178 return adj;
179}
180
181template float2x2 adjoint(const float2x2 &mat);
182template float3x3 adjoint(const float3x3 &mat);
183template float4x4 adjoint(const float4x4 &mat);
184template double2x2 adjoint(const double2x2 &mat);
185template double3x3 adjoint(const double3x3 &mat);
186template double4x4 adjoint(const double4x4 &mat);
187
190/* -------------------------------------------------------------------- */
194template<typename T, int Size>
196{
198 Eigen::Map<const Eigen::Matrix<T, Size, Size>> M(mat.base_ptr());
199 Eigen::Map<Eigen::Matrix<T, Size, Size>> R(result.base_ptr());
200 M.computeInverseWithCheck(R, r_success, 0.0f);
201 if (!r_success) {
202 R = R.Zero();
203 }
204 return result;
205}
206
207template float2x2 invert(const float2x2 &mat, bool &r_success);
208template float3x3 invert(const float3x3 &mat, bool &r_success);
209template float4x4 invert(const float4x4 &mat, bool &r_success);
210template double2x2 invert(const double2x2 &mat, bool &r_success);
211template double3x3 invert(const double3x3 &mat, bool &r_success);
212template double4x4 invert(const double4x4 &mat, bool &r_success);
213
214template<typename T, int Size>
216{
217 /* Start by trying normal inversion first. */
218 bool success;
219 MatBase<T, Size, Size> inv = invert<T, Size>(mat, success);
220 if (success) {
221 return inv;
222 }
223
232 VecBase<T, Size> S_val;
233
234 {
235 using namespace Eigen;
236 using MatrixT = Eigen::Matrix<T, Size, Size>;
237 using VectorT = Eigen::Matrix<T, Size, 1>;
238 /* Blender and Eigen matrices are both column-major by default.
239 * Since our matrix is squared, we can use thinU/V. */
245 using MatrixDynamicT = Eigen::Matrix<T, Dynamic, Dynamic>;
246 JacobiSVD<MatrixDynamicT, NoQRPreconditioner> svd(
247 Eigen::Map<const MatrixDynamicT>(mat.base_ptr(), Size, Size), ComputeThinU | ComputeThinV);
248
249 Eigen::Map<MatrixT>(U.base_ptr()) = svd.matrixU();
250 (Eigen::Map<VectorT>(S_val)) = svd.singularValues();
251 Eigen::Map<MatrixT>(V.base_ptr()) = svd.matrixV();
252 }
253
254 /* Invert or nullify component based on epsilon comparison. */
255 unroll<Size>([&](auto i) { S_val[i] = (S_val[i] < epsilon) ? T(0) : (T(1) / S_val[i]); });
256
257 W = from_scale<MatBase<T, Size, Size>>(S_val);
258 return (V * W) * transpose(U);
259}
260
261template float2x2 pseudo_invert(const float2x2 &mat, float epsilon);
262template float3x3 pseudo_invert(const float3x3 &mat, float epsilon);
263template float4x4 pseudo_invert(const float4x4 &mat, float epsilon);
264template double2x2 pseudo_invert(const double2x2 &mat, double epsilon);
265template double3x3 pseudo_invert(const double3x3 &mat, double epsilon);
266template double4x4 pseudo_invert(const double4x4 &mat, double epsilon);
267
270/* -------------------------------------------------------------------- */
283template<typename T>
284static void polar_decompose(const MatBase<T, 3, 3> &mat3,
285 MatBase<T, 3, 3> &r_U,
286 MatBase<T, 3, 3> &r_P)
287{
288 /* From svd decomposition (M = WSV*), we have:
289 * U = WV*
290 * P = VSV*
291 */
293 VecBase<T, 3> S_val;
294
295 {
296 using namespace Eigen;
297 using MatrixT = Eigen::Matrix<T, 3, 3>;
298 using VectorT = Eigen::Matrix<T, 3, 1>;
299 /* Blender and Eigen matrices are both column-major by default.
300 * Since our matrix is squared, we can use thinU/V. */
305 using MatrixDynamicT = Eigen::Matrix<T, Dynamic, Dynamic>;
306 JacobiSVD<MatrixDynamicT, NoQRPreconditioner> svd(
307 Eigen::Map<const MatrixDynamicT>(mat3.base_ptr(), 3, 3), ComputeThinU | ComputeThinV);
308
309 Eigen::Map<MatrixT>(W.base_ptr()) = svd.matrixU();
310 (Eigen::Map<VectorT>(S_val)) = svd.singularValues();
311 Map<MatrixT>(V.base_ptr()) = svd.matrixV();
312 }
313
314 MatBase<T, 3, 3> S = from_scale<MatBase<T, 3, 3>>(S_val);
316
317 r_U = W * Vt;
318 r_P = (V * S) * Vt;
319}
320
323/* -------------------------------------------------------------------- */
327template<typename T>
328MatBase<T, 3, 3> interpolate(const MatBase<T, 3, 3> &A, const MatBase<T, 3, 3> &B, T t)
329{
330 using Mat3T = MatBase<T, 3, 3>;
331 /* 'Rotation' component ('U' part of polar decomposition,
332 * the closest orthogonal matrix to M3 rot/scale
333 * transformation matrix), spherically interpolated. */
334 Mat3T U_A, U_B;
335 /* 'Scaling' component ('P' part of polar decomposition, i.e. scaling in U-defined space),
336 * linearly interpolated. */
337 Mat3T P_A, P_B;
338
339 polar_decompose(A, U_A, P_A);
340 polar_decompose(B, U_B, P_B);
341
342 /* Quaternions cannot represent an axis flip. If such a singularity is detected, choose a
343 * different decomposition of the matrix that still satisfies A = U_A * P_A but which has a
344 * positive determinant and thus no axis flips. This resolves #77154.
345 *
346 * Note that a flip of two axes is just a rotation of 180 degrees around the third axis, and
347 * three flipped axes are just an 180 degree rotation + a single axis flip. It is thus sufficient
348 * to solve this problem for single axis flips. */
349 if (is_negative(U_A)) {
350 U_A = -U_A;
351 P_A = -P_A;
352 }
353 if (is_negative(U_B)) {
354 U_B = -U_B;
355 P_B = -P_B;
356 }
357
360 QuaternionBase<T> quat = math::interpolate(quat_A, quat_B, t);
361 Mat3T U = from_rotation<Mat3T>(quat);
362
363 Mat3T P = interpolate_linear(P_A, P_B, t);
364 /* And we reconstruct rot/scale matrix from interpolated polar components */
365 return U * P;
366}
367
368template float3x3 interpolate(const float3x3 &a, const float3x3 &b, float t);
369template double3x3 interpolate(const double3x3 &a, const double3x3 &b, double t);
370
371template<typename T>
372MatBase<T, 4, 4> interpolate(const MatBase<T, 4, 4> &A, const MatBase<T, 4, 4> &B, T t)
373{
375 interpolate(MatBase<T, 3, 3>(A), MatBase<T, 3, 3>(B), t));
376
377 /* Location component, linearly interpolated. */
378 const auto &loc_a = static_cast<const MatBase<T, 4, 4> &>(A).location();
379 const auto &loc_b = static_cast<const MatBase<T, 4, 4> &>(B).location();
380 result.location() = interpolate(loc_a, loc_b, t);
381
382 return result;
383}
384
385template float4x4 interpolate(const float4x4 &a, const float4x4 &b, float t);
386template double4x4 interpolate(const double4x4 &a, const double4x4 &b, double t);
387
388template<typename T>
390{
391 using QuaternionT = QuaternionBase<T>;
392 using Vec3T = typename MatBase<T, 3, 3>::vec3_type;
393
394 Vec3T a_scale, b_scale;
395 QuaternionT a_quat, b_quat;
396 to_rot_scale<true>(a, a_quat, a_scale);
397 to_rot_scale<true>(b, b_quat, b_scale);
398
399 const Vec3T scale = interpolate(a_scale, b_scale, t);
400 const QuaternionT rotation = interpolate(a_quat, b_quat, t);
401 return from_rot_scale<MatBase<T, 3, 3>>(rotation, scale);
402}
403
404template float3x3 interpolate_fast(const float3x3 &a, const float3x3 &b, float t);
405template double3x3 interpolate_fast(const double3x3 &a, const double3x3 &b, double t);
406
407template<typename T>
409{
410 using QuaternionT = QuaternionBase<T>;
411 using Vec3T = typename MatBase<T, 3, 3>::vec3_type;
412
413 Vec3T a_loc, b_loc;
414 Vec3T a_scale, b_scale;
415 QuaternionT a_quat, b_quat;
416 to_loc_rot_scale<true>(a, a_loc, a_quat, a_scale);
417 to_loc_rot_scale<true>(b, b_loc, b_quat, b_scale);
418
419 const Vec3T location = interpolate(a_loc, b_loc, t);
420 const Vec3T scale = interpolate(a_scale, b_scale, t);
421 const QuaternionT rotation = interpolate(a_quat, b_quat, t);
422 return from_loc_rot_scale<MatBase<T, 4, 4>>(location, rotation, scale);
423}
424
425template float4x4 interpolate_fast(const float4x4 &a, const float4x4 &b, float t);
426template double4x4 interpolate_fast(const double4x4 &a, const double4x4 &b, double t);
427
430/* -------------------------------------------------------------------- */
435{
436 float3x3 n_mat = normalize(mat);
437 /* Rotate z-axis of matrix to z-axis. */
438 float3 z_axis = n_mat.z_axis();
439
440 /* Cross product with (0,0,1). */
441 float3 nor = normalize(float3(z_axis.y, -z_axis.x, 0.0f));
442
443 float ha = 0.5f * math::safe_acos(z_axis.z);
444 /* `nor` negative here, but why? */
445 Quaternion q1(math::cos(ha), -nor * math::sin(ha));
446
447 /* Rotate back x-axis from mat, using inverse q1. */
448 float3 x_axis = transform_point(conjugate(q1), n_mat.x_axis());
449
450 /* And align x-axes. */
451 float ha2 = 0.5f * math::atan2(x_axis.y, x_axis.x);
452 Quaternion q2(math::cos(ha2), 0.0f, 0.0f, math::sin(ha2));
453
454 return q1 * q2;
455}
456
459/* -------------------------------------------------------------------- */
463namespace detail {
464
465template void normalized_to_eul2(const float3x3 &mat,
466 Euler3Base<float> &eul1,
467 Euler3Base<float> &eul2);
468template void normalized_to_eul2(const float3x3 &mat,
469 EulerXYZBase<float> &eul1,
470 EulerXYZBase<float> &eul2);
471template void normalized_to_eul2(const double3x3 &mat,
472 EulerXYZBase<double> &eul1,
473 EulerXYZBase<double> &eul2);
474
475template QuaternionBase<float> normalized_to_quat_with_checks(const float3x3 &mat);
476template QuaternionBase<double> normalized_to_quat_with_checks(const double3x3 &mat);
477
478template MatBase<float, 2, 2> from_rotation(const AngleRadian &rotation);
479template MatBase<float, 3, 3> from_rotation(const AngleRadian &rotation);
480template MatBase<float, 3, 3> from_rotation(const EulerXYZ &rotation);
481template MatBase<float, 4, 4> from_rotation(const EulerXYZ &rotation);
482template MatBase<float, 3, 3> from_rotation(const Euler3 &rotation);
483template MatBase<float, 4, 4> from_rotation(const Euler3 &rotation);
484template MatBase<float, 3, 3> from_rotation(const Quaternion &rotation);
485template MatBase<float, 4, 4> from_rotation(const Quaternion &rotation);
486template MatBase<float, 3, 3> from_rotation(const AxisAngle &rotation);
487template MatBase<float, 4, 4> from_rotation(const AxisAngle &rotation);
488template MatBase<float, 3, 3> from_rotation(const AxisAngleCartesian &rotation);
489template MatBase<float, 4, 4> from_rotation(const AxisAngleCartesian &rotation);
490
491} // namespace detail
492
493template float3 transform_point(const float3x3 &mat, const float3 &point);
494template float3 transform_point(const float4x4 &mat, const float3 &point);
495template float3 transform_direction(const float3x3 &mat, const float3 &direction);
496template float3 transform_direction(const float4x4 &mat, const float3 &direction);
497template float3 project_point(const float4x4 &mat, const float3 &point);
498template float2 project_point(const float3x3 &mat, const float2 &point);
499
500namespace projection {
501
502template float4x4 orthographic(
503 float left, float right, float bottom, float top, float near_clip, float far_clip);
504template float4x4 perspective(
505 float left, float right, float bottom, float top, float near_clip, float far_clip);
507 float left, float right, float bottom, float top, float near_clip);
508
509} // namespace projection
510
513} // namespace blender::math
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Map
#define A0
Definition RandGen.cpp:26
#define A2
Definition RandGen.cpp:28
#define A1
Definition RandGen.cpp:27
#define U
#define A
unsigned int U
Definition btGjkEpa3.h:78
btMatrix3x3 adjoint() const
Return the adjoint of the matrix.
btMatrix3x3 transpose() const
Return the transpose of the matrix.
btScalar determinant() const
Return the determinant of the matrix.
static T sum(const btAlignedObjectArray< T > &items)
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
local_group_size(16, 16) .push_constant(Type b
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
#define M
#define T
#define B
#define R
MatBase< T, 4, 4 > perspective_infinite(T left, T right, T bottom, T top, T near_clip)
Create a perspective projection matrix using OpenGL coordinate convention: Maps each axis range to [-...
QuaternionBase< T > conjugate(const QuaternionBase< T > &a)
T cos(const AngleRadianBase< T > &a)
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
Quaternion to_quaternion_legacy(const float3x3 &mat)
MatBase< T, Size, Size > pseudo_invert(const MatBase< T, Size, Size > &mat, T epsilon=1e-8)
T interpolate(const T &a, const T &b, const FactorT &t)
T atan2(const T &y, const T &x)
MatBase< T, 3, 3 > interpolate_fast(const MatBase< T, 3, 3 > &a, const MatBase< T, 3, 3 > &b, T t)
T safe_acos(const T &a)
static void polar_decompose(const MatBase< T, 3, 3 > &mat3, MatBase< T, 3, 3 > &r_U, MatBase< T, 3, 3 > &r_P)
T sin(const AngleRadianBase< T > &a)
MatBase< T, NumCol, NumRow > interpolate_linear(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, T t)
MatBase< float, 2, 2 > float2x2
MatBase< double, 3, 3 > double3x3
MatBase< double, 2, 2 > double2x2
MatBase< T, B_NumCol, A_NumRow > operator*(const MatBase< T, A_NumCol, A_NumRow > &a, const MatBase< T, B_NumCol, B_NumRow > &b)
MatBase< double, 4, 4 > double4x4
Frequency::GEOMETRY nor[]
const T * base_ptr() const
ccl_device_inline float3 transform_direction(ccl_private const Transform *t, const float3 a)
Definition transform.h:94
CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a)
Definition transform.h:63
CCL_NAMESPACE_BEGIN struct Window V