Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2021-04-01 13:16:09 +0300
committerCampbell Barton <ideasman42@gmail.com>2021-04-01 13:34:35 +0300
commit8a144b73c0116a014ed6f081048ffb6b84255d0b (patch)
tree7aa8a52028b161c00f621364a105b5c7ae41bcda
parent3f24cfb9582e1c826406301d37808df7ca6aa64c (diff)
BLI_math: add orthogonalize_m#_zero_axes
Expose a this function to initialize any zeroed axes to an orthogonal vector based on other non-zeroed axes. This functionality already existed for `invert_m#_m#_safe_ortho`, expose as a public function as it's useful to be able to fill in zeroed axes of transformation matrices since they may be used in matrix multiplication which would create degenerate matrices.
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h3
-rw-r--r--source/blender/blenlib/intern/math_matrix.c151
2 files changed, 88 insertions, 66 deletions
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 6324963f06a..378095589e8 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -266,6 +266,9 @@ void orthogonalize_m4(float R[4][4], int axis);
void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize);
void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize);
+bool orthogonalize_m3_zero_axes(float R[3][3], const float unit_length);
+bool orthogonalize_m4_zero_axes(float R[4][4], const float unit_length);
+
bool is_orthogonal_m3(const float mat[3][3]);
bool is_orthogonal_m4(const float mat[4][4]);
bool is_orthonormal_m3(const float mat[3][3]);
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 2ada05d2965..d447da4de64 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1700,6 +1700,89 @@ void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize)
}
}
+/* -------------------------------------------------------------------- */
+/** \name Orthogonalize Matrix Zeroed Axes
+ *
+ * Set any zeroed axes to an orthogonal vector in relation to the other axes.
+ *
+ * Typically used so matrix inversion can be performed.
+ *
+ * \note If an object has a zero scaled axis, this function can be used to "clean" the matrix
+ * to behave as if the scale on that axis was `unit_length`. So it can be inverted
+ * or used in matrix multiply without creating degenerate matrices, see: T50103
+ * \{ */
+
+/**
+ * \return true if any axis needed to be modified.
+ */
+static bool orthogonalize_m3_zero_axes_impl(float *mat[3], const float unit_length)
+{
+ enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
+ int flag = 0;
+ for (int i = 0; i < 3; i++) {
+ flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
+ }
+
+ /* Either all or none are zero, either way we can't properly resolve this
+ * since we need to fill invalid axes from valid ones. */
+ if (ELEM(flag, 0, X | Y | Z)) {
+ return false;
+ }
+
+ switch (flag) {
+ case X | Y: {
+ ortho_v3_v3(mat[1], mat[2]);
+ ATTR_FALLTHROUGH;
+ }
+ case X: {
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+ break;
+ }
+
+ case Y | Z: {
+ ortho_v3_v3(mat[2], mat[0]);
+ ATTR_FALLTHROUGH;
+ }
+ case Y: {
+ cross_v3_v3v3(mat[1], mat[0], mat[2]);
+ break;
+ }
+
+ case Z | X: {
+ ortho_v3_v3(mat[0], mat[1]);
+ ATTR_FALLTHROUGH;
+ }
+ case Z: {
+ cross_v3_v3v3(mat[2], mat[0], mat[1]);
+ break;
+ }
+ default: {
+ BLI_assert(0); /* Unreachable! */
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (flag & (1 << i)) {
+ if (UNLIKELY(normalize_v3_length(mat[i], unit_length) == 0.0f)) {
+ mat[i][i] = unit_length;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool orthogonalize_m3_zero_axes(float m[3][3], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+bool orthogonalize_m4_zero_axes(float m[4][4], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+
+/** \} */
+
bool is_orthogonal_m3(const float m[3][3])
{
int i, j;
@@ -3196,68 +3279,6 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
* \{ */
/**
- * Return true if invert should be attempted again.
- *
- * \note Takes an array of points to be usable from 3x3 and 4x4 matrices.
- */
-static bool invert_m3_m3_safe_ortho_prepare(float *mat[3])
-{
- enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
- int flag = 0;
- for (int i = 0; i < 3; i++) {
- flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
- }
-
- /* Either all or none are zero, either way we can't properly resolve this
- * since we need to fill invalid axes from valid ones. */
- if (ELEM(flag, 0, X | Y | Z)) {
- return false;
- }
-
- switch (flag) {
- case X | Y: {
- ortho_v3_v3(mat[1], mat[2]);
- ATTR_FALLTHROUGH;
- }
- case X: {
- cross_v3_v3v3(mat[0], mat[1], mat[2]);
- break;
- }
-
- case Y | Z: {
- ortho_v3_v3(mat[2], mat[0]);
- ATTR_FALLTHROUGH;
- }
- case Y: {
- cross_v3_v3v3(mat[1], mat[0], mat[2]);
- break;
- }
-
- case Z | X: {
- ortho_v3_v3(mat[0], mat[1]);
- ATTR_FALLTHROUGH;
- }
- case Z: {
- cross_v3_v3v3(mat[2], mat[0], mat[1]);
- break;
- }
- default: {
- BLI_assert(0); /* Unreachable! */
- }
- }
-
- for (int i = 0; i < 3; i++) {
- if (flag & (1 << i)) {
- if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) {
- mat[i][i] = 1.0f;
- }
- }
- }
-
- return true;
-}
-
-/**
* A safe version of invert that uses valid axes, calculating the zero'd axis
* based on the non-zero ones.
*
@@ -3268,8 +3289,7 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
if (UNLIKELY(!invert_m4_m4(Ainv, A))) {
float Atemp[4][4];
copy_m4_m4(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m4_m4(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m4_zero_axes(Atemp, 1.0f) && invert_m4_m4(Ainv, Atemp)))) {
unit_m4(Ainv);
}
}
@@ -3280,8 +3300,7 @@ void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3])
if (UNLIKELY(!invert_m3_m3(Ainv, A))) {
float Atemp[3][3];
copy_m3_m3(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m3_m3(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m3_zero_axes(Atemp, 1.0f) && invert_m3_m3(Ainv, Atemp)))) {
unit_m3(Ainv);
}
}