Blender V4.3
BLI_math_matrix.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
11#include "BLI_math_base.hh"
14#include "BLI_math_vector.hh"
15
16namespace blender::math {
17
18/* -------------------------------------------------------------------- */
26template<typename T, int Size>
27[[nodiscard]] MatBase<T, Size, Size> invert(const MatBase<T, Size, Size> &mat, bool &r_success);
28
32template<typename T, int NumCol, int NumRow>
33[[nodiscard]] MatBase<T, NumCol, NumRow> transpose(const MatBase<T, NumRow, NumCol> &mat);
34
38template<typename T, int NumCol, int NumRow>
39[[nodiscard]] MatBase<T, NumCol, NumRow> normalize(const MatBase<T, NumCol, NumRow> &a);
40
45template<typename T, int NumCol, int NumRow, typename VectorT>
46[[nodiscard]] MatBase<T, NumCol, NumRow> normalize_and_get_size(
47 const MatBase<T, NumCol, NumRow> &a, VectorT &r_size);
48
53template<typename T, int Size> [[nodiscard]] T determinant(const MatBase<T, Size, Size> &mat);
54
58template<typename T, int Size>
59[[nodiscard]] MatBase<T, Size, Size> adjoint(const MatBase<T, Size, Size> &mat);
60
64template<typename T, int NumCol, int NumRow, typename VectorT>
65[[nodiscard]] MatBase<T, NumCol, NumRow> translate(const MatBase<T, NumCol, NumRow> &mat,
66 const VectorT &translation);
67
72template<typename T, int NumCol, int NumRow, typename RotationT>
74 const RotationT &rotation);
75
79template<typename T, int NumCol, int NumRow, typename VectorT>
81 const VectorT &scale);
82
86template<typename T, int NumCol, int NumRow>
89 T t);
90
106template<typename T>
107[[nodiscard]] MatBase<T, 3, 3> interpolate(const MatBase<T, 3, 3> &a,
108 const MatBase<T, 3, 3> &b,
109 T t);
110
119template<typename T>
120[[nodiscard]] MatBase<T, 4, 4> interpolate(const MatBase<T, 4, 4> &a,
121 const MatBase<T, 4, 4> &b,
122 T t);
123
135template<typename T>
137 const MatBase<T, 3, 3> &b,
138 T t);
139
152template<typename T>
154 const MatBase<T, 4, 4> &b,
155 T t);
156
167template<typename T, int Size>
169 T epsilon = 1e-8);
170
173/* -------------------------------------------------------------------- */
180template<typename MatT> [[nodiscard]] MatT from_location(const typename MatT::loc_type &location);
181
186template<typename MatT, int ScaleDim>
187[[nodiscard]] MatT from_scale(const VecBase<typename MatT::base_type, ScaleDim> &scale);
188
192template<typename MatT, typename RotationT>
193[[nodiscard]] MatT from_rotation(const RotationT &rotation);
194
198template<typename MatT, typename RotationT, typename VectorT>
199[[nodiscard]] MatT from_rot_scale(const RotationT &rotation, const VectorT &scale);
200
204template<typename MatT, typename RotationT>
205[[nodiscard]] MatT from_loc_rot(const typename MatT::loc_type &location,
206 const RotationT &rotation);
207
211template<typename MatT, int ScaleDim>
212[[nodiscard]] MatT from_loc_scale(const typename MatT::loc_type &location,
214
218template<typename MatT, typename RotationT, int ScaleDim>
219[[nodiscard]] MatT from_loc_rot_scale(const typename MatT::loc_type &location,
220 const RotationT &rotation,
222
228template<typename MatT, typename VectorT>
229[[nodiscard]] MatT from_orthonormal_axes(const VectorT forward, const VectorT up);
230
235template<typename MatT, typename VectorT>
236[[nodiscard]] MatT from_orthonormal_axes(const VectorT location,
237 const VectorT forward,
238 const VectorT up);
239
248template<typename MatT, typename VectorT> [[nodiscard]] MatT from_up_axis(const VectorT up);
249
259template<typename MatT> [[nodiscard]] MatT orthogonalize(const MatT &mat, const Axis axis);
260
266template<typename MatT, typename VectorT>
267[[nodiscard]] MatT from_origin_transform(const MatT &transform, const VectorT origin);
268
271/* -------------------------------------------------------------------- */
279template<typename T> [[nodiscard]] inline AngleRadianBase<T> to_angle(const MatBase<T, 2, 2> &mat);
280template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat);
281template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 4, 4> &mat);
282template<typename T>
283[[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order);
284template<typename T>
285[[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order);
286
294template<typename T>
295[[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
296 const EulerXYZBase<T> &reference);
297template<typename T>
298[[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
299 const EulerXYZBase<T> &reference);
300template<typename T>
301[[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
302 const Euler3Base<T> &reference);
303template<typename T>
304[[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
305 const Euler3Base<T> &reference);
306
310template<typename T>
311[[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 3, 3> &mat);
312template<typename T>
313[[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 4, 4> &mat);
314
321[[nodiscard]] Quaternion to_quaternion_legacy(const float3x3 &mat);
322
328template<bool AllowNegativeScale = false, typename T, int NumCol, int NumRow>
329[[nodiscard]] inline VecBase<T, 3> to_scale(const MatBase<T, NumCol, NumRow> &mat);
330template<bool AllowNegativeScale = false, typename T>
331[[nodiscard]] inline VecBase<T, 2> to_scale(const MatBase<T, 2, 2> &mat);
332
339template<bool AllowNegativeScale = false, typename T>
340inline void to_rot_scale(const MatBase<T, 2, 2> &mat,
341 AngleRadianBase<T> &r_rotation,
342 VecBase<T, 2> &r_scale);
343template<bool AllowNegativeScale = false, typename T>
344inline void to_loc_rot_scale(const MatBase<T, 3, 3> &mat,
345 VecBase<T, 2> &r_location,
346 AngleRadianBase<T> &r_rotation,
347 VecBase<T, 2> &r_scale);
348template<bool AllowNegativeScale = false, typename T, typename RotationT>
349inline void to_rot_scale(const MatBase<T, 3, 3> &mat,
350 RotationT &r_rotation,
351 VecBase<T, 3> &r_scale);
352template<bool AllowNegativeScale = false, typename T, typename RotationT>
353inline void to_loc_rot_scale(const MatBase<T, 4, 4> &mat,
354 VecBase<T, 3> &r_location,
355 RotationT &r_rotation,
356 VecBase<T, 3> &r_scale);
357
360/* -------------------------------------------------------------------- */
367template<typename T>
368[[nodiscard]] VecBase<T, 3> transform_point(const MatBase<T, 3, 3> &mat,
369 const VecBase<T, 3> &point);
370
374template<typename T>
375[[nodiscard]] VecBase<T, 3> transform_point(const MatBase<T, 4, 4> &mat,
376 const VecBase<T, 3> &point);
377
381template<typename T>
382[[nodiscard]] VecBase<T, 3> transform_direction(const MatBase<T, 3, 3> &mat,
383 const VecBase<T, 3> &direction);
384
388template<typename T>
389[[nodiscard]] VecBase<T, 3> transform_direction(const MatBase<T, 4, 4> &mat,
390 const VecBase<T, 3> &direction);
391
395template<typename MatT, typename VectorT>
396[[nodiscard]] VectorT project_point(const MatT &mat, const VectorT &point);
397
400/* -------------------------------------------------------------------- */
404namespace projection {
405
411template<typename T>
412[[nodiscard]] MatBase<T, 4, 4> orthographic(
413 T left, T right, T bottom, T top, T near_clip, T far_clip);
414
422template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top);
423
430template<typename T>
431[[nodiscard]] MatBase<T, 4, 4> perspective(
432 T left, T right, T bottom, T top, T near_clip, T far_clip);
433
440template<typename T>
442 T angle_left, T angle_right, T angle_bottom, T angle_top, T near_clip, T far_clip);
443
451template<typename T>
452[[nodiscard]] MatBase<T, 4, 4> perspective_infinite(T left, T right, T bottom, T top, T near_clip);
453
459template<typename T>
460[[nodiscard]] MatBase<T, 4, 4> translate(const MatBase<T, 4, 4> &mat, const VecBase<T, 2> &offset);
461
462} // namespace projection
463
466/* -------------------------------------------------------------------- */
476template<typename T, int Size> [[nodiscard]] bool is_negative(const MatBase<T, Size, Size> &mat)
477{
478 return determinant(mat) < T(0);
479}
480template<typename T> [[nodiscard]] bool is_negative(const MatBase<T, 4, 4> &mat);
481
485template<typename T, int NumCol, int NumRow>
486[[nodiscard]] inline bool is_equal(const MatBase<T, NumCol, NumRow> &a,
488 const T epsilon = T(0))
489{
490 for (int i = 0; i < NumCol; i++) {
491 for (int j = 0; j < NumRow; j++) {
492 if (math::abs(a[i][j] - b[i][j]) > epsilon) {
493 return false;
494 }
495 }
496 }
497 return true;
498}
499
503template<typename MatT> [[nodiscard]] inline bool is_orthogonal(const MatT &mat)
504{
505 if (math::abs(math::dot(mat.x_axis(), mat.y_axis())) > 1e-5f) {
506 return false;
507 }
508 if (math::abs(math::dot(mat.y_axis(), mat.z_axis())) > 1e-5f) {
509 return false;
510 }
511 if (math::abs(math::dot(mat.z_axis(), mat.x_axis())) > 1e-5f) {
512 return false;
513 }
514 return true;
515}
516
520template<typename MatT> [[nodiscard]] inline bool is_orthonormal(const MatT &mat)
521{
522 if (!is_orthogonal(mat)) {
523 return false;
524 }
525 if (math::abs(math::length_squared(mat.x_axis()) - 1) > 1e-5f) {
526 return false;
527 }
528 if (math::abs(math::length_squared(mat.y_axis()) - 1) > 1e-5f) {
529 return false;
530 }
531 if (math::abs(math::length_squared(mat.z_axis()) - 1) > 1e-5f) {
532 return false;
533 }
534 return true;
535}
536
540template<typename MatT> [[nodiscard]] inline bool is_uniformly_scaled(const MatT &mat)
541{
542 if (!is_orthogonal(mat)) {
543 return false;
544 }
545 using T = typename MatT::base_type;
546 const T eps = 1e-7;
547 const T x = math::length_squared(mat.x_axis());
548 const T y = math::length_squared(mat.y_axis());
549 const T z = math::length_squared(mat.z_axis());
550 return (math::abs(x - y) < eps) && math::abs(x - z) < eps;
551}
552
553template<typename T, int NumCol, int NumRow>
554inline bool is_zero(const MatBase<T, NumCol, NumRow> &mat)
555{
556 for (int i = 0; i < NumCol; i++) {
557 if (!is_zero(mat[i])) {
558 return false;
559 }
560 }
561 return true;
562}
563
566/* -------------------------------------------------------------------- */
570/* Implementation details. */
571namespace detail {
572
573template<typename T, int NumCol, int NumRow>
574[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const AngleRadianBase<T> &rotation);
575
576template<typename T, int NumCol, int NumRow>
577[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const EulerXYZBase<T> &rotation);
578
579template<typename T, int NumCol, int NumRow>
580[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const Euler3Base<T> &rotation);
581
582template<typename T, int NumCol, int NumRow>
583[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const QuaternionBase<T> &rotation);
584
585template<typename T, int NumCol, int NumRow>
587
588template<typename T, int NumCol, int NumRow>
589[[nodiscard]] MatBase<T, NumCol, NumRow> from_rotation(const CartesianBasis &rotation);
590
591template<typename T, int NumCol, int NumRow, typename AngleT>
593
594} // namespace detail
595
596/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
597template<typename T, int NumCol, int NumRow>
598[[nodiscard]] inline bool is_unit_scale(const MatBase<T, NumCol, NumRow> &m)
599{
600 for (int i = 0; i < NumCol; i++) {
601 if (!is_unit_scale(m[i])) {
602 return false;
603 }
604 }
605 return true;
606}
607
608template<typename T, int Size>
610{
611 bool success;
612 /* Explicit template parameter to please MSVC. */
613 return invert<T, Size>(mat, success);
614}
615
616template<typename T, int NumCol, int NumRow>
618{
620 unroll<NumCol>([&](auto i) { unroll<NumRow>([&](auto j) { result[i][j] = mat[j][i]; }); });
621 return result;
622}
623
624template<typename T, int NumCol, int NumRow, typename VectorT>
626 const VectorT &translation)
627{
628 using MatT = MatBase<T, NumCol, NumRow>;
629 BLI_STATIC_ASSERT(VectorT::type_length <= MatT::col_len - 1,
630 "Translation should be at least 1 column less than the matrix.");
631 constexpr int location_col = MatT::col_len - 1;
632 /* Avoid multiplying the last row if it exists.
633 * Allows using non square matrices like float3x2 and saves computation. */
634 using IntermediateVecT =
635 VecBase<typename MatT::base_type,
636 (MatT::row_len > MatT::col_len - 1) ? (MatT::col_len - 1) : MatT::row_len>;
637
638 MatT result = mat;
639 unroll<VectorT::type_length>([&](auto c) {
640 *reinterpret_cast<IntermediateVecT *>(
641 &result[location_col]) += translation[c] *
642 *reinterpret_cast<const IntermediateVecT *>(&mat[c]);
643 });
644 return result;
645}
646
647template<typename T, int NumCol, int NumRow, typename AngleT>
649 const AxisAngleBase<T, AngleT> &rotation)
650{
651 using MatT = MatBase<T, NumCol, NumRow>;
652 using Vec3T = typename MatT::vec3_type;
653 const T angle_sin = sin(rotation.angle());
654 const T angle_cos = cos(rotation.angle());
655 const Vec3T &axis_vec = rotation.axis();
656
657 MatT result = mat;
658 /* axis_vec is given to be normalized. */
659 if (axis_vec.x == T(1)) {
660 unroll<MatT::row_len>([&](auto c) {
661 result[2][c] = -angle_sin * mat[1][c] + angle_cos * mat[2][c];
662 result[1][c] = angle_cos * mat[1][c] + angle_sin * mat[2][c];
663 });
664 }
665 else if (axis_vec.y == T(1)) {
666 unroll<MatT::row_len>([&](auto c) {
667 result[0][c] = angle_cos * mat[0][c] - angle_sin * mat[2][c];
668 result[2][c] = angle_sin * mat[0][c] + angle_cos * mat[2][c];
669 });
670 }
671 else if (axis_vec.z == T(1)) {
672 unroll<MatT::row_len>([&](auto c) {
673 result[0][c] = angle_cos * mat[0][c] + angle_sin * mat[1][c];
674 result[1][c] = -angle_sin * mat[0][c] + angle_cos * mat[1][c];
675 });
676 }
677 else {
678 /* Un-optimized case. Arbitrary */
679 result *= from_rotation<MatT>(rotation);
680 }
681 return result;
682}
683
684template<typename T, int NumCol, int NumRow, typename VectorT>
686 const VectorT &scale)
687{
688 BLI_STATIC_ASSERT(VectorT::type_length <= NumCol,
689 "Scale should be less or equal to the matrix in column count.");
690 MatBase<T, NumCol, NumRow> result = mat;
691 unroll<VectorT::type_length>([&](auto c) { result[c] *= scale[c]; });
692 return result;
693}
694
695template<typename T, int NumCol, int NumRow>
698 T t)
699{
701 unroll<NumCol>([&](auto c) { result[c] = interpolate(a[c], b[c], t); });
702 return result;
703}
704
705template<typename T,
706 int NumCol,
707 int NumRow,
708 int SrcNumCol,
709 int SrcNumRow,
710 int SrcStartCol,
711 int SrcStartRow,
712 int SrcAlignment>
721
722template<typename T,
723 int NumCol,
724 int NumRow,
725 int SrcNumCol,
726 int SrcNumRow,
727 int SrcStartCol,
728 int SrcStartRow,
729 int SrcAlignment,
730 typename VectorT>
733 &a,
734 VectorT &r_size)
735{
736 BLI_STATIC_ASSERT(VectorT::type_length == NumCol,
737 "r_size dimension should be equal to matrix column count.");
739 unroll<NumCol>([&](auto i) { result[i] = math::normalize_and_get_length(a[i], r_size[i]); });
740 return result;
741}
742
743template<typename T, int NumCol, int NumRow>
745{
747 unroll<NumCol>([&](auto i) { result[i] = math::normalize(a[i]); });
748 return result;
749}
750
751template<typename T, int NumCol, int NumRow, typename VectorT>
753 const MatBase<T, NumCol, NumRow> &a, VectorT &r_size)
754{
755 BLI_STATIC_ASSERT(VectorT::type_length == NumCol,
756 "r_size dimension should be equal to matrix column count.");
758 unroll<NumCol>([&](auto i) { result[i] = math::normalize_and_get_length(a[i], r_size[i]); });
759 return result;
760}
761
762namespace detail {
763
765{
767 return AngleRadianBase(mat[0][0], mat[0][1]);
768}
769
770template<typename T>
772{
774
775 const T cy = math::hypot(mat[0][0], mat[0][1]);
776 if (cy > T(16) * std::numeric_limits<T>::epsilon()) {
777 eul1.x() = math::atan2(mat[1][2], mat[2][2]);
778 eul1.y() = math::atan2(-mat[0][2], cy);
779 eul1.z() = math::atan2(mat[0][1], mat[0][0]);
780
781 eul2.x() = math::atan2(-mat[1][2], -mat[2][2]);
782 eul2.y() = math::atan2(-mat[0][2], -cy);
783 eul2.z() = math::atan2(-mat[0][1], -mat[0][0]);
784 }
785 else {
786 eul1.x() = math::atan2(-mat[2][1], mat[1][1]);
787 eul1.y() = math::atan2(-mat[0][2], cy);
788 eul1.z() = 0.0f;
789
790 eul2 = eul1;
791 }
792}
793template<typename T>
795{
797 const int i_index = eul1.i_index();
798 const int j_index = eul1.j_index();
799 const int k_index = eul1.k_index();
800
801 const T cy = math::hypot(mat[i_index][i_index], mat[i_index][j_index]);
802 if (cy > T(16) * std::numeric_limits<T>::epsilon()) {
803 eul1.i() = math::atan2(mat[j_index][k_index], mat[k_index][k_index]);
804 eul1.j() = math::atan2(-mat[i_index][k_index], cy);
805 eul1.k() = math::atan2(mat[i_index][j_index], mat[i_index][i_index]);
806
807 eul2.i() = math::atan2(-mat[j_index][k_index], -mat[k_index][k_index]);
808 eul2.j() = math::atan2(-mat[i_index][k_index], -cy);
809 eul2.k() = math::atan2(-mat[i_index][j_index], -mat[i_index][i_index]);
810 }
811 else {
812 eul1.i() = math::atan2(-mat[k_index][j_index], mat[j_index][j_index]);
813 eul1.j() = math::atan2(-mat[i_index][k_index], cy);
814 eul1.k() = 0.0f;
815
816 eul2 = eul1;
817 }
818
819 if (eul1.parity()) {
820 eul1 = -eul1;
821 eul2 = -eul2;
822 }
823}
824
825/* Using explicit template instantiations in order to reduce compilation time. */
826extern template void normalized_to_eul2(const float3x3 &mat,
827 Euler3Base<float> &eul1,
828 Euler3Base<float> &eul2);
829extern template void normalized_to_eul2(const float3x3 &mat,
831 EulerXYZBase<float> &eul2);
832extern template void normalized_to_eul2(const double3x3 &mat,
835
837{
839 /* Caller must ensure matrices aren't negative for valid results, see: #24291, #94231. */
841
843
844 /* Method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949
845 * with an additional `sqrtf(..)` for higher precision result.
846 * Removing the `sqrt` causes tests to fail unless the precision is set to 1e-6 or larger. */
847
848 if (mat[2][2] < 0.0f) {
849 if (mat[0][0] > mat[1][1]) {
850 const T trace = 1.0f + mat[0][0] - mat[1][1] - mat[2][2];
851 T s = 2.0f * math::sqrt(trace);
852 if (mat[1][2] < mat[2][1]) {
853 /* Ensure W is non-negative for a canonical result. */
854 s = -s;
855 }
856 q.x = 0.25f * s;
857 s = 1.0f / s;
858 q.w = (mat[1][2] - mat[2][1]) * s;
859 q.y = (mat[0][1] + mat[1][0]) * s;
860 q.z = (mat[2][0] + mat[0][2]) * s;
861 if (UNLIKELY((trace == 1.0f) && (q.w == 0.0f && q.y == 0.0f && q.z == 0.0f))) {
862 /* Avoids the need to normalize the degenerate case. */
863 q.x = 1.0f;
864 }
865 }
866 else {
867 const T trace = 1.0f - mat[0][0] + mat[1][1] - mat[2][2];
868 T s = 2.0f * math::sqrt(trace);
869 if (mat[2][0] < mat[0][2]) {
870 /* Ensure W is non-negative for a canonical result. */
871 s = -s;
872 }
873 q.y = 0.25f * s;
874 s = 1.0f / s;
875 q.w = (mat[2][0] - mat[0][2]) * s;
876 q.x = (mat[0][1] + mat[1][0]) * s;
877 q.z = (mat[1][2] + mat[2][1]) * s;
878 if (UNLIKELY((trace == 1.0f) && (q.w == 0.0f && q.x == 0.0f && q.z == 0.0f))) {
879 /* Avoids the need to normalize the degenerate case. */
880 q.y = 1.0f;
881 }
882 }
883 }
884 else {
885 if (mat[0][0] < -mat[1][1]) {
886 const T trace = 1.0f - mat[0][0] - mat[1][1] + mat[2][2];
887 T s = 2.0f * math::sqrt(trace);
888 if (mat[0][1] < mat[1][0]) {
889 /* Ensure W is non-negative for a canonical result. */
890 s = -s;
891 }
892 q.z = 0.25f * s;
893 s = 1.0f / s;
894 q.w = (mat[0][1] - mat[1][0]) * s;
895 q.x = (mat[2][0] + mat[0][2]) * s;
896 q.y = (mat[1][2] + mat[2][1]) * s;
897 if (UNLIKELY((trace == 1.0f) && (q.w == 0.0f && q.x == 0.0f && q.y == 0.0f))) {
898 /* Avoids the need to normalize the degenerate case. */
899 q.z = 1.0f;
900 }
901 }
902 else {
903 /* NOTE(@ideasman42): A zero matrix will fall through to this block,
904 * needed so a zero scaled matrices to return a quaternion without rotation, see: #101848.
905 */
906 const T trace = 1.0f + mat[0][0] + mat[1][1] + mat[2][2];
907 T s = 2.0f * math::sqrt(trace);
908 q.w = 0.25f * s;
909 s = 1.0f / s;
910 q.x = (mat[1][2] - mat[2][1]) * s;
911 q.y = (mat[2][0] - mat[0][2]) * s;
912 q.z = (mat[0][1] - mat[1][0]) * s;
913 if (UNLIKELY((trace == 1.0f) && (q.x == 0.0f && q.y == 0.0f && q.z == 0.0f))) {
914 /* Avoids the need to normalize the degenerate case. */
915 q.w = 1.0f;
916 }
917 }
918 }
919
920 BLI_assert(!(q.w < 0.0f));
922 return q;
923}
924
926{
927 const T det = math::determinant(mat);
928 if (UNLIKELY(!std::isfinite(det))) {
930 }
931 else if (UNLIKELY(det < T(0))) {
932 return normalized_to_quat_fast(-mat);
933 }
934 return normalized_to_quat_fast(mat);
935}
936
937/* Using explicit template instantiations in order to reduce compilation time. */
940
941template<typename T, int NumCol, int NumRow>
943{
944 using MatT = MatBase<T, NumCol, NumRow>;
945 using DoublePrecision = typename TypeTraits<T>::DoublePrecision;
946 const DoublePrecision cos_i = math::cos(DoublePrecision(rotation.x().radian()));
947 const DoublePrecision cos_j = math::cos(DoublePrecision(rotation.y().radian()));
948 const DoublePrecision cos_k = math::cos(DoublePrecision(rotation.z().radian()));
949 const DoublePrecision sin_i = math::sin(DoublePrecision(rotation.x().radian()));
950 const DoublePrecision sin_j = math::sin(DoublePrecision(rotation.y().radian()));
951 const DoublePrecision sin_k = math::sin(DoublePrecision(rotation.z().radian()));
952 const DoublePrecision cos_i_cos_k = cos_i * cos_k;
953 const DoublePrecision cos_i_sin_k = cos_i * sin_k;
954 const DoublePrecision sin_i_cos_k = sin_i * cos_k;
955 const DoublePrecision sin_i_sin_k = sin_i * sin_k;
956
957 MatT mat = MatT::identity();
958 mat[0][0] = T(cos_j * cos_k);
959 mat[1][0] = T(sin_j * sin_i_cos_k - cos_i_sin_k);
960 mat[2][0] = T(sin_j * cos_i_cos_k + sin_i_sin_k);
961
962 mat[0][1] = T(cos_j * sin_k);
963 mat[1][1] = T(sin_j * sin_i_sin_k + cos_i_cos_k);
964 mat[2][1] = T(sin_j * cos_i_sin_k - sin_i_cos_k);
965
966 mat[0][2] = T(-sin_j);
967 mat[1][2] = T(cos_j * sin_i);
968 mat[2][2] = T(cos_j * cos_i);
969 return mat;
970}
971
972template<typename T, int NumCol, int NumRow>
974{
975 using MatT = MatBase<T, NumCol, NumRow>;
976 const int i_index = rotation.i_index();
977 const int j_index = rotation.j_index();
978 const int k_index = rotation.k_index();
979#if 1 /* Reference. */
980 EulerXYZBase<T> euler_xyz(rotation.ijk());
981 const MatT mat = from_rotation<T, NumCol, NumRow>(rotation.parity() ? -euler_xyz : euler_xyz);
982 MatT result = MatT::identity();
983 result[i_index][i_index] = mat[0][0];
984 result[j_index][i_index] = mat[1][0];
985 result[k_index][i_index] = mat[2][0];
986 result[i_index][j_index] = mat[0][1];
987 result[j_index][j_index] = mat[1][1];
988 result[k_index][j_index] = mat[2][1];
989 result[i_index][k_index] = mat[0][2];
990 result[j_index][k_index] = mat[1][2];
991 result[k_index][k_index] = mat[2][2];
992#else
993 /* TODO(fclem): Manually inline and check performance difference. */
994#endif
995 return result;
996}
997
998template<typename T, int NumCol, int NumRow>
1000{
1001 using MatT = MatBase<T, NumCol, NumRow>;
1002 using DoublePrecision = typename TypeTraits<T>::DoublePrecision;
1003 const DoublePrecision q0 = numbers::sqrt2 * DoublePrecision(rotation.w);
1004 const DoublePrecision q1 = numbers::sqrt2 * DoublePrecision(rotation.x);
1005 const DoublePrecision q2 = numbers::sqrt2 * DoublePrecision(rotation.y);
1006 const DoublePrecision q3 = numbers::sqrt2 * DoublePrecision(rotation.z);
1007
1008 const DoublePrecision qda = q0 * q1;
1009 const DoublePrecision qdb = q0 * q2;
1010 const DoublePrecision qdc = q0 * q3;
1011 const DoublePrecision qaa = q1 * q1;
1012 const DoublePrecision qab = q1 * q2;
1013 const DoublePrecision qac = q1 * q3;
1014 const DoublePrecision qbb = q2 * q2;
1015 const DoublePrecision qbc = q2 * q3;
1016 const DoublePrecision qcc = q3 * q3;
1017
1018 MatT mat = MatT::identity();
1019 mat[0][0] = T(1.0 - qbb - qcc);
1020 mat[0][1] = T(qdc + qab);
1021 mat[0][2] = T(-qdb + qac);
1022
1023 mat[1][0] = T(-qdc + qab);
1024 mat[1][1] = T(1.0 - qaa - qcc);
1025 mat[1][2] = T(qda + qbc);
1026
1027 mat[2][0] = T(qdb + qac);
1028 mat[2][1] = T(-qda + qbc);
1029 mat[2][2] = T(1.0 - qaa - qbb);
1030 return mat;
1031}
1032
1033/* Not technically speaking a simple rotation, but a whole transform. */
1034template<typename T, int NumCol, int NumRow>
1036{
1037 using MatT = MatBase<T, NumCol, NumRow>;
1038 BLI_assert(is_normalized(rotation));
1045 /* Follow the paper notation. */
1046 const QuaternionBase<T> &c0 = rotation.quat;
1047 const QuaternionBase<T> &ce = rotation.trans;
1048 const T &w0 = c0.w, &x0 = c0.x, &y0 = c0.y, &z0 = c0.z;
1049 const T &we = ce.w, &xe = ce.x, &ye = ce.y, &ze = ce.z;
1050 /* Rotation. */
1051 MatT mat = from_rotation<T, NumCol, NumRow>(c0);
1052 /* Translation. */
1053 mat[3][0] = T(2) * (-we * x0 + xe * w0 - ye * z0 + ze * y0);
1054 mat[3][1] = T(2) * (-we * y0 + xe * z0 + ye * w0 - ze * x0);
1055 mat[3][2] = T(2) * (-we * z0 - xe * y0 + ye * x0 + ze * w0);
1056 /* Scale. */
1057 if (rotation.scale_weight != T(0)) {
1058 mat.template view<4, 4>() = mat * rotation.scale;
1059 }
1060 return mat;
1061}
1062
1063template<typename T, int NumCol, int NumRow>
1065{
1066 using MatT = MatBase<T, NumCol, NumRow>;
1067 MatT mat = MatT::identity();
1068 mat.x_axis() = to_vector<VecBase<T, 3>>(rotation.axes.x);
1069 mat.y_axis() = to_vector<VecBase<T, 3>>(rotation.axes.y);
1070 mat.z_axis() = to_vector<VecBase<T, 3>>(rotation.axes.z);
1071 return mat;
1072}
1073
1074template<typename T, int NumCol, int NumRow, typename AngleT>
1076{
1077 using MatT = MatBase<T, NumCol, NumRow>;
1078 using Vec3T = typename MatT::vec3_type;
1079 const T angle_sin = sin(rotation.angle());
1080 const T angle_cos = cos(rotation.angle());
1081 const Vec3T &axis = rotation.axis();
1082
1084
1085 T ico = (T(1) - angle_cos);
1086 Vec3T nsi = axis * angle_sin;
1087
1088 Vec3T n012 = (axis * axis) * ico;
1089 T n_01 = (axis[0] * axis[1]) * ico;
1090 T n_02 = (axis[0] * axis[2]) * ico;
1091 T n_12 = (axis[1] * axis[2]) * ico;
1092
1093 MatT mat = from_scale<MatT>(n012 + angle_cos);
1094 mat[0][1] = n_01 + nsi[2];
1095 mat[0][2] = n_02 - nsi[1];
1096 mat[1][0] = n_01 - nsi[2];
1097 mat[1][2] = n_12 + nsi[0];
1098 mat[2][0] = n_02 + nsi[1];
1099 mat[2][1] = n_12 - nsi[0];
1100 return mat;
1101}
1102
1103template<typename T, int NumCol, int NumRow>
1105{
1106 using MatT = MatBase<T, NumCol, NumRow>;
1107 const T cos_i = cos(rotation);
1108 const T sin_i = sin(rotation);
1109
1110 MatT mat = MatT::identity();
1111 mat[0][0] = cos_i;
1112 mat[1][0] = -sin_i;
1113
1114 mat[0][1] = sin_i;
1115 mat[1][1] = cos_i;
1116 return mat;
1117}
1118
1119/* Using explicit template instantiations in order to reduce compilation time. */
1120extern template MatBase<float, 2, 2> from_rotation(const AngleRadian &rotation);
1121extern template MatBase<float, 3, 3> from_rotation(const AngleRadian &rotation);
1122extern template MatBase<float, 3, 3> from_rotation(const EulerXYZ &rotation);
1123extern template MatBase<float, 4, 4> from_rotation(const EulerXYZ &rotation);
1124extern template MatBase<float, 3, 3> from_rotation(const Euler3 &rotation);
1125extern template MatBase<float, 4, 4> from_rotation(const Euler3 &rotation);
1126extern template MatBase<float, 3, 3> from_rotation(const Quaternion &rotation);
1127extern template MatBase<float, 4, 4> from_rotation(const Quaternion &rotation);
1128extern template MatBase<float, 3, 3> from_rotation(const AxisAngle &rotation);
1129extern template MatBase<float, 4, 4> from_rotation(const AxisAngle &rotation);
1131extern template MatBase<float, 4, 4> from_rotation(const AxisAngleCartesian &rotation);
1132
1133} // namespace detail
1134
1135template<typename T> [[nodiscard]] inline AngleRadianBase<T> to_angle(const MatBase<T, 2, 2> &mat)
1136{
1137 return detail::normalized_to_angle(mat);
1138}
1139
1140template<typename T>
1141[[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 3, 3> &mat, EulerOrder order)
1142{
1143 Euler3Base<T> eul1(order), eul2(order);
1144 detail::normalized_to_eul2(mat, eul1, eul2);
1145 /* Return best, which is just the one with lowest values in it. */
1146 return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 :
1147 eul1;
1148}
1149
1150template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 3, 3> &mat)
1151{
1152 EulerXYZBase<T> eul1, eul2;
1153 detail::normalized_to_eul2(mat, eul1, eul2);
1154 /* Return best, which is just the one with lowest values in it. */
1155 return (length_manhattan(VecBase<T, 3>(eul1)) > length_manhattan(VecBase<T, 3>(eul2))) ? eul2 :
1156 eul1;
1157}
1158
1159template<typename T>
1160[[nodiscard]] inline Euler3Base<T> to_euler(const MatBase<T, 4, 4> &mat, EulerOrder order)
1161{
1162 /* TODO(fclem): Avoid the copy with 3x3 ref. */
1163 return to_euler<T>(MatBase<T, 3, 3>(mat), order);
1164}
1165
1166template<typename T> [[nodiscard]] inline EulerXYZBase<T> to_euler(const MatBase<T, 4, 4> &mat)
1167{
1168 /* TODO(fclem): Avoid the copy with 3x3 ref. */
1169 return to_euler<T>(MatBase<T, 3, 3>(mat));
1170}
1171
1172template<typename T>
1173[[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
1174 const Euler3Base<T> &reference)
1175{
1176 Euler3Base<T> eul1(reference.order()), eul2(reference.order());
1177 detail::normalized_to_eul2(mat, eul1, eul2);
1178 eul1 = eul1.wrapped_around(reference);
1179 eul2 = eul2.wrapped_around(reference);
1180 /* Return best, which is just the one with lowest values it in. */
1181 return (length_manhattan(VecBase<T, 3>(eul1) - VecBase<T, 3>(reference)) >
1182 length_manhattan(VecBase<T, 3>(eul2) - VecBase<T, 3>(reference))) ?
1183 eul2 :
1184 eul1;
1185}
1186
1187template<typename T>
1188[[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 3, 3> &mat,
1189 const EulerXYZBase<T> &reference)
1190{
1191 EulerXYZBase<T> eul1, eul2;
1192 detail::normalized_to_eul2(mat, eul1, eul2);
1193 eul1 = eul1.wrapped_around(reference);
1194 eul2 = eul2.wrapped_around(reference);
1195 /* Return best, which is just the one with lowest values it in. */
1196 return (length_manhattan(VecBase<T, 3>(eul1) - VecBase<T, 3>(reference)) >
1197 length_manhattan(VecBase<T, 3>(eul2) - VecBase<T, 3>(reference))) ?
1198 eul2 :
1199 eul1;
1200}
1201
1202template<typename T>
1203[[nodiscard]] inline Euler3Base<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
1204 const Euler3Base<T> &reference)
1205{
1206 /* TODO(fclem): Avoid the copy with 3x3 ref. */
1207 return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
1208}
1209
1210template<typename T>
1211[[nodiscard]] inline EulerXYZBase<T> to_nearest_euler(const MatBase<T, 4, 4> &mat,
1212 const EulerXYZBase<T> &reference)
1213{
1214 /* TODO(fclem): Avoid the copy with 3x3 ref. */
1215 return to_euler<T>(MatBase<T, 3, 3>(mat), reference);
1216}
1217
1218template<typename T>
1219[[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 3, 3> &mat)
1220{
1222}
1223
1224template<typename T>
1225[[nodiscard]] inline QuaternionBase<T> to_quaternion(const MatBase<T, 4, 4> &mat)
1226{
1227 /* TODO(fclem): Avoid the copy with 3x3 ref. */
1229}
1230
1235template<typename T>
1237{
1238 /* Conversion to quaternion asserts when the matrix contains some kinds of shearing, conversion
1239 * to euler does not. */
1240 /* TODO: Find a better algorithm that can convert untrusted matrices to quaternions directly. */
1241 return to_quaternion(to_euler(mat));
1242}
1243
1244template<bool AllowNegativeScale, typename T, int NumCol, int NumRow>
1245[[nodiscard]] inline VecBase<T, 3> to_scale(const MatBase<T, NumCol, NumRow> &mat)
1246{
1247 VecBase<T, 3> result = {length(mat.x_axis()), length(mat.y_axis()), length(mat.z_axis())};
1248 if constexpr (AllowNegativeScale) {
1249 if (UNLIKELY(is_negative(mat))) {
1250 result = -result;
1251 }
1252 }
1253 return result;
1254}
1255
1256template<bool AllowNegativeScale, typename T>
1257[[nodiscard]] inline VecBase<T, 2> to_scale(const MatBase<T, 2, 2> &mat)
1258{
1259 VecBase<T, 2> result = {length(mat.x), length(mat.y)};
1260 if constexpr (AllowNegativeScale) {
1261 if (UNLIKELY(is_negative(mat))) {
1262 result = -result;
1263 }
1264 }
1265 return result;
1266}
1267
1268/* Implementation details. Use `to_euler` and `to_quaternion` instead. */
1269namespace detail {
1270
1271template<typename T>
1272inline void to_rotation(const MatBase<T, 2, 2> &mat, AngleRadianBase<T> &r_rotation)
1273{
1274 r_rotation = to_angle<T>(mat);
1275}
1276
1277template<typename T>
1278inline void to_rotation(const MatBase<T, 3, 3> &mat, QuaternionBase<T> &r_rotation)
1279{
1280 r_rotation = to_quaternion<T>(mat);
1281}
1282
1283template<typename T>
1284inline void to_rotation(const MatBase<T, 3, 3> &mat, EulerXYZBase<T> &r_rotation)
1285{
1286 r_rotation = to_euler<T>(mat);
1287}
1288
1289template<typename T>
1290inline void to_rotation(const MatBase<T, 3, 3> &mat, Euler3Base<T> &r_rotation)
1291{
1292 r_rotation = to_euler<T>(mat, r_rotation.order());
1293}
1294
1295} // namespace detail
1296
1297template<bool AllowNegativeScale, typename T>
1298inline void to_rot_scale(const MatBase<T, 2, 2> &mat,
1299 AngleRadianBase<T> &r_rotation,
1300 VecBase<T, 2> &r_scale)
1301{
1302 MatBase<T, 2, 2> normalized_mat = normalize_and_get_size(mat, r_scale);
1303 if constexpr (AllowNegativeScale) {
1304 if (UNLIKELY(is_negative(normalized_mat))) {
1305 normalized_mat = -normalized_mat;
1306 r_scale = -r_scale;
1307 }
1308 }
1309 detail::to_rotation<T>(normalized_mat, r_rotation);
1310}
1311
1312template<bool AllowNegativeScale, typename T>
1313inline void to_loc_rot_scale(const MatBase<T, 3, 3> &mat,
1314 VecBase<T, 2> &r_location,
1315 AngleRadianBase<T> &r_rotation,
1316 VecBase<T, 2> &r_scale)
1317{
1318 r_location = mat.location();
1319 to_rot_scale<AllowNegativeScale>(MatBase<T, 2, 2>(mat), r_rotation, r_scale);
1320}
1321
1322template<bool AllowNegativeScale, typename T, typename RotationT>
1323inline void to_rot_scale(const MatBase<T, 3, 3> &mat,
1324 RotationT &r_rotation,
1325 VecBase<T, 3> &r_scale)
1326{
1327 MatBase<T, 3, 3> normalized_mat = normalize_and_get_size(mat, r_scale);
1328 if constexpr (AllowNegativeScale) {
1329 if (UNLIKELY(is_negative(normalized_mat))) {
1330 normalized_mat = -normalized_mat;
1331 r_scale = -r_scale;
1332 }
1333 }
1334 detail::to_rotation<T>(normalized_mat, r_rotation);
1335}
1336
1337template<bool AllowNegativeScale, typename T, typename RotationT>
1338inline void to_loc_rot_scale(const MatBase<T, 4, 4> &mat,
1339 VecBase<T, 3> &r_location,
1340 RotationT &r_rotation,
1341 VecBase<T, 3> &r_scale)
1342{
1343 r_location = mat.location();
1344 to_rot_scale<AllowNegativeScale>(MatBase<T, 3, 3>(mat), r_rotation, r_scale);
1345}
1346
1351template<bool AllowNegativeScale, typename T, typename RotationT>
1353 VecBase<T, 3> &r_location,
1354 RotationT &r_rotation,
1355 VecBase<T, 3> &r_scale)
1356{
1357 EulerXYZBase<T> euler_rotation;
1358 to_loc_rot_scale<AllowNegativeScale>(mat, r_location, euler_rotation, r_scale);
1359 if constexpr (std::is_same_v<std::decay_t<RotationT>, QuaternionBase<T>>) {
1360 r_rotation = to_quaternion(euler_rotation);
1361 }
1362 else {
1363 r_rotation = RotationT(euler_rotation);
1364 }
1365}
1366
1367template<typename MatT> [[nodiscard]] MatT from_location(const typename MatT::loc_type &location)
1368{
1369 MatT mat = MatT::identity();
1370 mat.location() = location;
1371 return mat;
1372}
1373
1374template<typename MatT, int ScaleDim>
1376{
1377 BLI_STATIC_ASSERT(ScaleDim <= MatT::min_dim,
1378 "Scale dimension should fit the matrix diagonal length.");
1379 MatT result{};
1381 [&](auto i) { result[i][i] = (i < ScaleDim) ? scale[i] : typename MatT::base_type(1); });
1382 return result;
1383}
1384
1385template<typename MatT, typename RotationT>
1386[[nodiscard]] MatT from_rotation(const RotationT &rotation)
1387{
1389}
1390
1391template<typename MatT, typename RotationT, typename VectorT>
1392[[nodiscard]] MatT from_rot_scale(const RotationT &rotation, const VectorT &scale)
1393{
1394 return from_rotation<MatT>(rotation) * from_scale<MatT>(scale);
1395}
1396
1397template<typename MatT, typename RotationT, int ScaleDim>
1398[[nodiscard]] MatT from_loc_rot_scale(const typename MatT::loc_type &location,
1399 const RotationT &rotation,
1401{
1402 using MatRotT =
1404 MatT mat = MatT(from_rot_scale<MatRotT>(rotation, scale));
1405 mat.location() = location;
1406 return mat;
1407}
1408
1409template<typename MatT, typename RotationT>
1410[[nodiscard]] MatT from_loc_rot(const typename MatT::loc_type &location, const RotationT &rotation)
1411{
1412 using MatRotT =
1414 MatT mat = MatT(from_rotation<MatRotT>(rotation));
1415 mat.location() = location;
1416 return mat;
1417}
1418
1419template<typename MatT, int ScaleDim>
1420[[nodiscard]] MatT from_loc_scale(const typename MatT::loc_type &location,
1422{
1423 MatT mat = MatT(from_scale<MatT>(scale));
1424 mat.location() = location;
1425 return mat;
1426}
1427
1428template<typename MatT, typename VectorT>
1429[[nodiscard]] MatT from_orthonormal_axes(const VectorT forward, const VectorT up)
1430{
1431 BLI_assert(is_unit_scale(forward));
1433
1434 /* TODO(fclem): This is wrong. Forward is Y. */
1435 MatT matrix;
1436 matrix.x_axis() = forward;
1437 /* Beware of handedness! Blender uses right-handedness.
1438 * Resulting matrix should have determinant of 1. */
1439 matrix.y_axis() = math::cross(up, forward);
1440 matrix.z_axis() = up;
1441 return matrix;
1442}
1443
1444template<typename MatT, typename VectorT>
1445[[nodiscard]] MatT from_orthonormal_axes(const VectorT location,
1446 const VectorT forward,
1447 const VectorT up)
1448{
1450 MatT matrix = MatT(from_orthonormal_axes<Mat3x3>(forward, up));
1451 matrix.location() = location;
1452 return matrix;
1453}
1454
1455template<typename MatT, typename VectorT> [[nodiscard]] MatT from_up_axis(const VectorT up)
1456{
1458 using T = typename MatT::base_type;
1459 using Vec3T = VecBase<T, 3>;
1460 /* Duff, Tom, et al. "Building an orthonormal basis, revisited." JCGT 6.1 (2017). */
1461 T sign = up.z >= T(0) ? T(1) : T(-1);
1462 T a = T(-1) / (sign + up.z);
1463 T b = up.x * up.y * a;
1464
1465 MatBase<T, 3, 3> basis;
1466 basis.x_axis() = Vec3T(1.0f + sign * square(up.x) * a, sign * b, -sign * up.x);
1467 basis.y_axis() = Vec3T(b, sign + square(up.y) * a, -up.y);
1468 basis.z_axis() = up;
1469 return MatT(basis);
1470}
1471
1472template<typename MatT> [[nodiscard]] MatT orthogonalize(const MatT &mat, const Axis axis)
1473{
1474 using T = typename MatT::base_type;
1475 using Vec3T = VecBase<T, 3>;
1476 Vec3T scale;
1478 R.x = normalize_and_get_length(mat.x_axis(), scale.x);
1479 R.y = normalize_and_get_length(mat.y_axis(), scale.y);
1480 R.z = normalize_and_get_length(mat.z_axis(), scale.z);
1481 /* NOTE(fclem) This is a direct port from `orthogonalize_m4()`.
1482 * To select the secondary axis, it checks if the candidate axis is not colinear.
1483 * The issue is that the candidate axes are not normalized so this dot product
1484 * check is kind of pointless.
1485 * Because of this, the target axis could still be colinear but pass the check. */
1486#if 1 /* Reproduce C API behavior. Do not normalize other axes. */
1487 switch (axis) {
1488 case Axis::X:
1489 R.y = mat.y_axis();
1490 R.z = mat.z_axis();
1491 break;
1492 case Axis::Y:
1493 R.x = mat.x_axis();
1494 R.z = mat.z_axis();
1495 break;
1496 case Axis::Z:
1497 R.x = mat.x_axis();
1498 R.y = mat.y_axis();
1499 break;
1500 }
1501#endif
1502
1508 switch (axis) {
1509 case Axis::X:
1510 if (dot(R.x, R.y) < T(1)) {
1511 R.z = normalize(cross(R.x, R.y));
1512 R.y = cross(R.z, R.x);
1513 }
1514 else if (dot(R.x, R.z) < T(1)) {
1515 R.y = normalize(cross(R.z, R.x));
1516 R.z = cross(R.x, R.y);
1517 }
1518 else {
1519 R.z = normalize(cross(R.x, Vec3T(R.x.y, R.x.z, R.x.x)));
1520 R.y = cross(R.z, R.x);
1521 }
1522 break;
1523 case Axis::Y:
1524 if (dot(R.y, R.x) < T(1)) {
1525 R.z = normalize(cross(R.x, R.y));
1526 R.x = cross(R.y, R.z);
1527 }
1528 /* FIXME(fclem): THIS IS WRONG. Should be dot(R.y, R.z). Following C code for now... */
1529 else if (dot(R.x, R.z) < T(1)) {
1530 R.x = normalize(cross(R.y, R.z));
1531 R.z = cross(R.x, R.y);
1532 }
1533 else {
1534 R.x = normalize(cross(R.y, Vec3T(R.y.y, R.y.z, R.y.x)));
1535 R.z = cross(R.x, R.y);
1536 }
1537 break;
1538 case Axis::Z:
1539 if (dot(R.z, R.x) < T(1)) {
1540 R.y = normalize(cross(R.z, R.x));
1541 R.x = cross(R.y, R.z);
1542 }
1543 else if (dot(R.z, R.y) < T(1)) {
1544 R.x = normalize(cross(R.y, R.z));
1545 R.y = cross(R.z, R.x);
1546 }
1547 else {
1548 R.x = normalize(cross(Vec3T(R.z.y, R.z.z, R.z.x), R.z));
1549 R.y = cross(R.z, R.x);
1550 }
1551 break;
1552 default:
1554 break;
1555 }
1556 /* Reapply the lost scale. */
1557 R.x *= scale.x;
1558 R.y *= scale.y;
1559 R.z *= scale.z;
1560
1561 MatT result(R);
1562 result.location() = mat.location();
1563 return result;
1564}
1565
1566template<typename MatT, typename VectorT>
1567[[nodiscard]] MatT from_origin_transform(const MatT &transform, const VectorT origin)
1568{
1569 return from_location<MatT>(origin) * transform * from_location<MatT>(-origin);
1570}
1571
1572template<typename T>
1574{
1575 return mat * point;
1576}
1577
1578template<typename T>
1580{
1581 return mat.template view<3, 3>() * point + mat.location();
1582}
1583
1584template<typename T>
1586{
1587 return mat * direction;
1588}
1589
1590template<typename T>
1592{
1593 return mat.template view<3, 3>() * direction;
1594}
1595
1596template<typename T, int N, int NumRow>
1598{
1599 VecBase<T, N + 1> tmp(point, T(1));
1600 tmp = mat * tmp;
1601 /* Absolute value to not flip the frustum upside down behind the camera. */
1602 return VecBase<T, N>(tmp) / math::abs(tmp[N]);
1603}
1604
1605extern template float3 transform_point(const float3x3 &mat, const float3 &point);
1606extern template float3 transform_point(const float4x4 &mat, const float3 &point);
1607extern template float3 transform_direction(const float3x3 &mat, const float3 &direction);
1608extern template float3 transform_direction(const float4x4 &mat, const float3 &direction);
1609extern template float3 project_point(const float4x4 &mat, const float3 &point);
1610extern template float2 project_point(const float3x3 &mat, const float2 &point);
1611
1612namespace projection {
1613
1614template<typename T>
1615MatBase<T, 4, 4> orthographic(T left, T right, T bottom, T top, T near_clip, T far_clip)
1616{
1617 const T x_delta = right - left;
1618 const T y_delta = top - bottom;
1619 const T z_delta = far_clip - near_clip;
1620
1622 if (x_delta != 0 && y_delta != 0 && z_delta != 0) {
1623 mat[0][0] = T(2.0) / x_delta;
1624 mat[3][0] = -(right + left) / x_delta;
1625 mat[1][1] = T(2.0) / y_delta;
1626 mat[3][1] = -(top + bottom) / y_delta;
1627 mat[2][2] = -T(2.0) / z_delta; /* NOTE: negate Z. */
1628 mat[3][2] = -(far_clip + near_clip) / z_delta;
1629 }
1630 return mat;
1631}
1632
1633template<typename T>
1634MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top, T near_clip)
1635{
1636 const T x_delta = right - left;
1637 const T y_delta = top - bottom;
1638
1640 if (x_delta != 0 && y_delta != 0) {
1641 mat[0][0] = T(2.0) / x_delta;
1642 mat[3][0] = -(right + left) / x_delta;
1643 mat[1][1] = T(2.0) / y_delta;
1644 mat[3][1] = -(top + bottom) / y_delta;
1645 /* Page 17. Choosing an epsilon for 32 bit floating-point precision. */
1646 constexpr float eps = 2.4e-7f;
1647 /* From "Projection Matrix Tricks" by Eric Lengyel GDC 2007.
1648 * Following same procedure as the reference but for orthographic matrix.
1649 * This avoids degenerate matrix (0 determinant). */
1650 mat[2][2] = -eps;
1651 mat[3][2] = -1.0f - eps * near_clip;
1652 }
1653 return mat;
1654}
1655
1656template<typename T>
1657MatBase<T, 4, 4> perspective(T left, T right, T bottom, T top, T near_clip, T far_clip)
1658{
1659 const T x_delta = right - left;
1660 const T y_delta = top - bottom;
1661 const T z_delta = far_clip - near_clip;
1662
1664 if (x_delta != 0 && y_delta != 0 && z_delta != 0) {
1665 mat[0][0] = near_clip * T(2.0) / x_delta;
1666 mat[1][1] = near_clip * T(2.0) / y_delta;
1667 mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
1668 mat[2][1] = (top + bottom) / y_delta;
1669 mat[2][2] = -(far_clip + near_clip) / z_delta;
1670 mat[2][3] = -1.0f;
1671 mat[3][2] = (-2.0f * near_clip * far_clip) / z_delta;
1672 mat[3][3] = 0.0f;
1673 }
1674 return mat;
1675}
1676
1677template<typename T>
1678MatBase<T, 4, 4> perspective_infinite(T left, T right, T bottom, T top, T near_clip)
1679{
1680 const T x_delta = right - left;
1681 const T y_delta = top - bottom;
1682
1683 /* From "Projection Matrix Tricks" by Eric Lengyel GDC 2007. */
1685 if (x_delta != 0 && y_delta != 0) {
1686 mat[0][0] = near_clip * T(2.0) / x_delta;
1687 mat[1][1] = near_clip * T(2.0) / y_delta;
1688 mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
1689 mat[2][1] = (top + bottom) / y_delta;
1690 /* Page 17. Choosing an epsilon for 32 bit floating-point precision. */
1691 constexpr float eps = 2.4e-7f;
1692 mat[2][2] = -1.0f;
1693 mat[2][3] = (eps - 1.0f);
1694 mat[3][2] = (eps - 2.0f) * near_clip;
1695 mat[3][3] = 0.0f;
1696 }
1697 return mat;
1698}
1699
1700template<typename T>
1702 T angle_left, T angle_right, T angle_bottom, T angle_top, T near_clip, T far_clip)
1703{
1704 MatBase<T, 4, 4> mat = perspective(math::tan(angle_left),
1705 math::tan(angle_right),
1706 math::tan(angle_bottom),
1707 math::tan(angle_top),
1708 near_clip,
1709 far_clip);
1710 mat[0][0] /= near_clip;
1711 mat[1][1] /= near_clip;
1712 return mat;
1713}
1714
1715template<typename T>
1716[[nodiscard]] MatBase<T, 4, 4> translate(const MatBase<T, 4, 4> &mat, const VecBase<T, 2> &offset)
1717{
1718 MatBase<T, 4, 4> result = mat;
1719 const bool is_perspective = mat[2][3] == -1.0f;
1720 const bool is_perspective_infinite = mat[2][2] == -1.0f;
1721 if (is_perspective || is_perspective_infinite) {
1722 result[2][0] -= mat[0][0] * offset.x / math::length(float3(mat[0][0], mat[1][0], mat[2][0]));
1723 result[2][1] -= mat[1][1] * offset.y / math::length(float3(mat[0][1], mat[1][1], mat[2][1]));
1724 }
1725 else {
1726 result[3][0] += offset.x;
1727 result[3][1] += offset.y;
1728 }
1729 return result;
1730}
1731
1732extern template float4x4 orthographic(
1733 float left, float right, float bottom, float top, float near_clip, float far_clip);
1734extern template float4x4 perspective(
1735 float left, float right, float bottom, float top, float near_clip, float far_clip);
1736
1737} // namespace projection
1738
1741} // namespace blender::math
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNLIKELY(x)
static AppView * view
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 Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
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.
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static constexpr Value Z
static constexpr Value X
static constexpr Value Y
local_group_size(16, 16) .push_constant(Type b
static int left
#define N
#define T
#define R
QuaternionBase< T > normalized_to_quat_with_checks(const MatBase< T, 3, 3 > &mat)
void normalized_to_eul2(const MatBase< T, 3, 3 > &mat, EulerXYZBase< T > &eul1, EulerXYZBase< T > &eul2)
void to_rotation(const MatBase< T, 2, 2 > &mat, AngleRadianBase< T > &r_rotation)
QuaternionBase< T > normalized_to_quat_fast(const MatBase< T, 3, 3 > &mat)
AngleRadianBase< T > normalized_to_angle(const MatBase< T, 2, 2 > &mat)
MatBase< T, NumCol, NumRow > from_rotation(const AngleRadianBase< T > &rotation)
MatBase< T, 4, 4 > orthographic(T left, T right, T bottom, T top, T near_clip, T far_clip)
Create an orthographic projection matrix using OpenGL coordinate convention: Maps each axis range to ...
MatBase< T, 4, 4 > orthographic_infinite(T left, T right, T bottom, T top)
Create an orthographic projection matrix using OpenGL coordinate convention: Maps each axis range to ...
MatBase< T, 4, 4 > perspective_fov(T angle_left, T angle_right, T angle_bottom, T angle_top, T near_clip, T far_clip)
Create a perspective projection matrix using OpenGL coordinate convention: Maps each axis range to [-...
MatBase< T, 4, 4 > translate(const MatBase< T, 4, 4 > &mat, const VecBase< T, 2 > &offset)
Translate a projection matrix after creation in the screen plane. Usually used for anti-aliasing jitt...
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 [-...
MatBase< T, 4, 4 > perspective(T left, T right, T bottom, T top, T near_clip, T far_clip)
Create a perspective projection matrix using OpenGL coordinate convention: Maps each axis range to [-...
AngleRadianBase< T > to_angle(const MatBase< T, 2, 2 > &mat)
T length_squared(const VecBase< T, Size > &a)
T cos(const AngleRadianBase< T > &a)
bool is_normalized(const DualQuaternionBase< T > &dq)
T sqrt(const T &a)
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
bool is_orthonormal(const MatT &mat)
T sign(const T &a)
Quaternion to_quaternion_legacy(const float3x3 &mat)
MatT from_up_axis(const VectorT up)
T to_vector(const Axis axis)
T length(const VecBase< T, Size > &a)
MatT from_loc_rot(const typename MatT::loc_type &location, const RotationT &rotation)
MatT from_loc_scale(const typename MatT::loc_type &location, const VecBase< typename MatT::base_type, ScaleDim > &scale)
bool is_uniformly_scaled(const MatT &mat)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
bool is_unit_scale(const MatBase< T, NumCol, NumRow > &m)
bool is_negative(const MatBase< T, Size, Size > &mat)
MatBase< T, Size, Size > pseudo_invert(const MatBase< T, Size, Size > &mat, T epsilon=1e-8)
T length_manhattan(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
VectorT project_point(const MatT &mat, const VectorT &point)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
T interpolate(const T &a, const T &b, const FactorT &t)
EulerXYZBase< T > to_nearest_euler(const MatBase< T, 3, 3 > &mat, const EulerXYZBase< T > &reference)
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)
MatBase< T, NumCol, NumRow > translate(const MatBase< T, NumCol, NumRow > &mat, const VectorT &translation)
QuaternionBase< T > normalized_to_quaternion_safe(const MatBase< T, 3, 3 > &mat)
MatBase< T, NumCol, NumRow > normalize_and_get_size(const MatBase< T, NumCol, NumRow > &a, VectorT &r_size)
MatT from_origin_transform(const MatT &transform, const VectorT origin)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
T square(const T &a)
bool is_equal(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, const T epsilon=T(0))
T sin(const AngleRadianBase< T > &a)
void to_rot_scale(const MatBase< T, 2, 2 > &mat, AngleRadianBase< T > &r_rotation, VecBase< T, 2 > &r_scale)
MatT orthogonalize(const MatT &mat, const Axis axis)
T abs(const T &a)
MatBase< T, NumCol, NumRow > interpolate_linear(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, T t)
T tan(const AngleRadianBase< T > &a)
void to_loc_rot_scale_safe(const MatBase< T, 4, 4 > &mat, VecBase< T, 3 > &r_location, RotationT &r_rotation, VecBase< T, 3 > &r_scale)
MatT from_location(const typename MatT::loc_type &location)
void to_loc_rot_scale(const MatBase< T, 3, 3 > &mat, VecBase< T, 2 > &r_location, AngleRadianBase< T > &r_rotation, VecBase< T, 2 > &r_scale)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
MatT from_rot_scale(const RotationT &rotation, const VectorT &scale)
MatBase< T, NumCol, NumRow > rotate(const MatBase< T, NumCol, NumRow > &mat, const RotationT &rotation)
CartesianBasis from_orthonormal_axes(const AxisSigned forward, const AxisSigned up)
MatT from_rotation(const RotationT &rotation)
T hypot(const T &y, const T &x)
T determinant(const MatBase< T, Size, Size > &mat)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
bool is_orthogonal(const MatT &mat)
void unroll(Fn fn)
Definition BLI_unroll.hh:21
VecBase< float, 3 > float3
const btScalar eps
Definition poly34.cpp:11
static MatBase identity()
Euler3Base wrapped_around(const Euler3Base &reference) const
const EulerOrder & order() const
EulerXYZBase wrapped_around(const EulerXYZBase &reference) const