using System; using UnityEngine; namespace Util { class MathUtil { public static bool Decompose(Matrix4x4 matrix, out Vector3 scale, out Quaternion rotation, out Vector3 translation) { Matrix4x4 rotationMatrix; Decompose(matrix, out scale, out rotationMatrix, out translation); RotationMatrix(rotationMatrix, out rotation); return true; } private static bool Decompose(Matrix4x4 matrix, out Vector3 scale, out Matrix4x4 rotation, out Vector3 translation) { //Get the translation. translation.x = matrix.m30; translation.y = matrix.m31; translation.z = matrix.m32; //Scaling is the length of the rows. scale.x = (float)Math.Sqrt((matrix.m00 * matrix.m00) + (matrix.m01 * matrix.m01) + (matrix.m02 * matrix.m02)); scale.y = (float)Math.Sqrt((matrix.m10 * matrix.m10) + (matrix.m11 * matrix.m11) + (matrix.m12 * matrix.m12)); scale.z = (float)Math.Sqrt((matrix.m20 * matrix.m20) + (matrix.m21 * matrix.m21) + (matrix.m22 * matrix.m22)); //If any of the scaling factors are zero, than the rotation matrix can not exist. if (IsZero(scale.x) || IsZero(scale.y) || IsZero(scale.z)) { rotation = Matrix4x4.identity; return false; } Vector3 at = new Vector3(matrix.m20 / scale.z, matrix.m21 / scale.z, matrix.m22 / scale.z); Vector3 up = Vector3.Cross(at, new Vector3(matrix.m00 / scale.x, matrix.m01 / scale.x, matrix.m02 / scale.x)); Vector3 right = Vector3.Cross(up, at); rotation = Matrix4x4.identity; rotation.m00 = right.x; rotation.m01 = right.y; rotation.m02 = right.z; rotation.m10 = up.x; rotation.m11 = up.y; rotation.m12 = up.z; rotation.m20 = at.x; rotation.m21 = at.y; rotation.m22 = at.z; // In case of reflexions scale.x = Vector3.Dot(right, new Vector3(matrix.m00, matrix.m01, matrix.m02)) > 0.0f ? scale.x : -scale.x; scale.y = Vector3.Dot(up, new Vector3(matrix.m10, matrix.m11, matrix.m12)) > 0.0f ? scale.y : -scale.y; scale.z = Vector3.Dot(at, new Vector3(matrix.m20, matrix.m21, matrix.m22)) > 0.0f ? scale.z : -scale.z; return true; } private static void RotationMatrix(Matrix4x4 matrix, out Quaternion result) { float sqrt; float half; float scale = matrix.m00 + matrix.m11 + matrix.m22; if (scale > 0.0f) { sqrt = (float)Math.Sqrt(scale + 1.0f); result.w = sqrt * 0.5f; sqrt = 0.5f / sqrt; result.x = (matrix.m12 - matrix.m21) * sqrt; result.y = (matrix.m20 - matrix.m02) * sqrt; result.z = (matrix.m01 - matrix.m10) * sqrt; } else if ((matrix.m00 >= matrix.m11) && (matrix.m00 >= matrix.m22)) { sqrt = (float)Math.Sqrt(1.0f + matrix.m00 - matrix.m11 - matrix.m22); half = 0.5f / sqrt; result.x = 0.5f * sqrt; result.y = (matrix.m01 + matrix.m10) * half; result.z = (matrix.m02 + matrix.m20) * half; result.w = (matrix.m12 - matrix.m21) * half; } else if (matrix.m11 > matrix.m22) { sqrt = (float)Math.Sqrt(1.0f + matrix.m11 - matrix.m00 - matrix.m22); half = 0.5f / sqrt; result.x = (matrix.m10 + matrix.m01) * half; result.y = 0.5f * sqrt; result.z = (matrix.m21 + matrix.m12) * half; result.w = (matrix.m20 - matrix.m02) * half; } else { sqrt = (float)Math.Sqrt(1.0f + matrix.m22 - matrix.m00 - matrix.m11); half = 0.5f / sqrt; result.x = (matrix.m20 + matrix.m02) * half; result.y = (matrix.m21 + matrix.m12) * half; result.z = 0.5f * sqrt; result.w = (matrix.m01 - matrix.m10) * half; } } private static bool IsZero(float a) { return Math.Abs(a) < 1e-6f; } public static bool isSimilar(float a, float b) { return a - b <= 0.001; } public static float Interpolate(float startX, float endX, float start, float end, float tanPoint1, float tanPoint2, float t, out float tangent) { // Catmull-Rom splines are Hermite curves with special tangent values. // Hermite curve formula: // (2t^3 - 3t^2 + 1) * p0 + (t^3 - 2t^2 + t) * m0 + (-2t^3 + 3t^2) * p1 + (t^3 - t^2) * m1 // For points p0 and p1 passing through points m0 and m1 interpolated over t = [0, 1] // Tangent M[k] = (P[k+1] - P[k-1]) / 2 // With [] indicating subscript float value = (2.0f * t * t * t - 3.0f * t * t + 1.0f) * start + (t * t * t - 2.0f * t * t + t) * tanPoint1 + (-2.0f * t * t * t + 3.0f * t * t) * end + (t * t * t - t * t) * tanPoint2; // Calculate tangents // p'(t) = (6t² - 6t)p0 + (3t² - 4t + 1)m0 + (-6t² + 6t)p1 + (3t² - 2t)m1 tangent = (6 * t * t - 6 * t) * start + (3 * t * t - 4 * t + 1) * tanPoint1 + (-6 * t * t + 6 * t) * end + (3 * t * t - 2 * t) * tanPoint2; tangent /= (endX - startX); return value; } } }