using GLTF.Schema; using UnityEngine; namespace UnityGLTF.Extensions { public static class SchemaExtensions { public static void GetUnityTRSProperties(this Node node, out Vector3 position, out Quaternion rotation, out Vector3 scale) { Vector3 localPosition, localScale; Quaternion localRotation; if (!node.UseTRS) { GetTRSProperties(node.Matrix, out localPosition, out localRotation, out localScale); } else { localPosition = node.Translation.ToUnityVector3(); localRotation = node.Rotation.ToUnityQuaternion(); localScale = node.Scale.ToUnityVector3(); } position = localPosition.switchHandedness(); rotation = localRotation.switchHandedness(); scale = new Vector3(localScale.x, localScale.y, localScale.z); } public static void SetUnityTransform(this Node node, Transform transform, bool useLocal=true) { Vector3 position = useLocal ? transform.localPosition : transform.position; node.Translation = new GLTF.Math.Vector3(position.x, position.y, -position.z); Quaternion rotation = useLocal ? transform.localRotation : transform.rotation; node.Rotation = new GLTF.Math.Quaternion(rotation.x, rotation.y, -rotation.z, -rotation.w); Vector3 scale = useLocal ? transform.localScale : transform.lossyScale; node.Scale = new GLTF.Math.Vector3(scale.x, scale.y, scale.z); } // todo: move to utility class public static void GetTRSProperties(GLTF.Math.Matrix4x4 mat, out Vector3 position, out Quaternion rotation, out Vector3 scale) { position = mat.GetColumnV3(3); scale = new Vector3( mat.GetColumnV3(0).magnitude, mat.GetColumnV3(1).magnitude, mat.GetColumnV3(2).magnitude ); rotation = Quaternion.LookRotation(mat.GetColumnV3(2), mat.GetColumnV3(1)); } #if false public static SamplerId GetSamplerId(this GLTFRoot root, UnityEngine.Texture textureObj) { for (var i = 0; i < root.Samplers.Count; i++) { bool filterIsNearest = root.Samplers[i].MinFilter == MinFilterMode.Nearest || root.Samplers[i].MinFilter == MinFilterMode.NearestMipmapNearest || root.Samplers[i].MinFilter == MinFilterMode.LinearMipmapNearest; bool filterIsLinear = root.Samplers[i].MinFilter == MinFilterMode.Linear || root.Samplers[i].MinFilter == MinFilterMode.NearestMipmapLinear; bool filterMatched = textureObj.filterMode == FilterMode.Point && filterIsNearest || textureObj.filterMode == FilterMode.Bilinear && filterIsLinear || textureObj.filterMode == FilterMode.Trilinear && root.Samplers[i].MinFilter == MinFilterMode.LinearMipmapLinear; bool wrapMatched = textureObj.wrapMode == TextureWrapMode.Clamp && root.Samplers[i].WrapS == GLTFSerialization.WrapMode.ClampToEdge || textureObj.wrapMode == TextureWrapMode.Repeat && root.Samplers[i].WrapS != GLTFSerialization.WrapMode.ClampToEdge; if(filterMatched && wrapMatched) { return new SamplerId { Id = i, Root = root }; } } return null; } //todo blgross unity public static ImageId GetImageId(this GLTFRoot root, UnityEngine.Texture textureObj) { for (var i = 0; i < Images.Count; i++) { if (Images[i].Contents == textureObj) { return new ImageId { Id = i, Root = this }; } } return null; } #endif public static Vector4 GetColumn(this GLTF.Math.Matrix4x4 mat, uint columnNum) { switch (columnNum) { case 0: { return new Vector4(mat.M11, mat.M12, mat.M13, mat.M14); } case 1: { return new Vector4(mat.M21, mat.M22, mat.M23, mat.M24); } case 2: { return new Vector4(mat.M31, mat.M32, mat.M33, mat.M34); } case 3: { return new Vector4(mat.M41, mat.M42, mat.M43, mat.M44); } default: throw new System.Exception("column num is out of bounds"); } } public static Vector3 GetColumnV3(this GLTF.Math.Matrix4x4 mat, uint columnNum) { switch (columnNum) { case 0: { return new Vector3(mat.M11, mat.M21, mat.M31); } case 1: { return new Vector3(mat.M12, mat.M22, mat.M32); } case 2: { return new Vector3(mat.M13, mat.M23, mat.M33); } case 3: { return new Vector3(mat.M14, mat.M24, mat.M34); } default: throw new System.Exception("column num is out of bounds"); } } public static Vector2 ToUnityVector2(this GLTF.Math.Vector2 vec3) { return new Vector2(vec3.X, vec3.Y); } public static Vector2[] ToUnityVector2(this GLTF.Math.Vector2[] inVecArr) { Vector2[] outVecArr = new Vector2[inVecArr.Length]; for (int i = 0; i < inVecArr.Length; ++i) { outVecArr[i] = inVecArr[i].ToUnityVector2(); } return outVecArr; } public static Vector3 ToUnityVector3(this GLTF.Math.Vector3 vec3) { return new Vector3(vec3.X, vec3.Y, vec3.Z); } public static Vector3[] ToUnityVector3(this GLTF.Math.Vector3[] inVecArr, bool switchHandedness=false) { Vector3[] outVecArr = new Vector3[inVecArr.Length]; for (int i = 0; i < inVecArr.Length; ++i) { outVecArr[i] = inVecArr[i].ToUnityVector3(); if (switchHandedness) outVecArr[i] = outVecArr[i].switchHandedness(); } return outVecArr; } public static GLTF.Math.Vector4 ToGLTFVector4(this Vector4 vec4) { return new GLTF.Math.Vector4(vec4.x, vec4.y, vec4.z, vec4.w); } public static Vector4 ToUnityVector4(this GLTF.Math.Vector4 vec4) { return new Vector4(vec4.X, vec4.Y, vec4.Z, vec4.W); } public static Vector4[] ToUnityVector4(this GLTF.Math.Vector4[] inVecArr, bool switchHandedness = false) { Vector4[] outVecArr = new Vector4[inVecArr.Length]; for (int i = 0; i < inVecArr.Length; ++i) { outVecArr[i] = inVecArr[i].ToUnityVector4(); if (switchHandedness) outVecArr[i] = outVecArr[i].switchHandedness(); } return outVecArr; } public static UnityEngine.Color ToUnityColor(this GLTF.Math.Color color) { return new UnityEngine.Color(color.R, color.G, color.B, color.A); } public static GLTF.Math.Color ToNumericsColor(this UnityEngine.Color color) { return new GLTF.Math.Color(color.r, color.g, color.b, color.a); } public static UnityEngine.Color[] ToUnityColor(this GLTF.Math.Color[] inColorArr) { UnityEngine.Color[] outColorArr = new UnityEngine.Color[inColorArr.Length]; for (int i = 0; i < inColorArr.Length; ++i) { outColorArr[i] = inColorArr[i].ToUnityColor(); } return outColorArr; } public static Quaternion ToUnityQuaternion(this GLTF.Math.Quaternion quaternion) { return new Quaternion(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W); } public static Matrix4x4 ToUnityMatrix(this GLTF.Math.Matrix4x4 matrix) { Matrix4x4 mat = new Matrix4x4(); mat.SetColumn(0, matrix.GetColumn(0)); mat.SetColumn(1, matrix.GetColumn(1)); mat.SetColumn(2, matrix.GetColumn(2)); mat.SetColumn(3, matrix.GetColumn(3)); return mat; } public static GLTF.Math.Matrix4x4 ToGLTFMAtrix(this Matrix4x4 matrix) { return new GLTF.Math.Matrix4x4( matrix.GetColumn(0).ToGLTFVector4(), matrix.GetColumn(1).ToGLTFVector4(), matrix.GetColumn(2).ToGLTFVector4(), matrix.GetColumn(3).ToGLTFVector4() ); } public static Vector3 switchHandedness(this Vector3 input) { return new Vector3(input.x, input.y, -input.z); } public static Vector4 switchHandedness(this Vector4 input) { return new Vector4(input.x, input.y, -input.z, -input.w); } public static Quaternion switchHandedness(this Quaternion input) { return new Quaternion(input.x, input.y, -input.z, -input.w); } public static Matrix4x4 switchHandedness(this Matrix4x4 matrix) { Vector3 position = matrix.GetColumn(3).switchHandedness(); Quaternion rotation = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1)).switchHandedness(); Vector3 scale = new Vector3(matrix.GetColumn(0).magnitude, matrix.GetColumn(1).magnitude, matrix.GetColumn(2).magnitude); float epsilon = 0.00001f; // Some issues can occurs with non uniform scales if (Mathf.Abs(scale.x - scale.y) > epsilon || Mathf.Abs(scale.y - scale.z) > epsilon || Mathf.Abs(scale.x - scale.z) > epsilon) { Debug.LogWarning("A matrix with non uniform scale is being converted from left to right handed system. This code is not working correctly in this case"); } // Handle negative scale component in matrix decomposition if (Matrix4x4.Determinant(matrix) < 0) { Quaternion rot = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1)); Matrix4x4 corr = Matrix4x4.TRS(matrix.GetColumn(3), rot, Vector3.one).inverse; Matrix4x4 extractedScale = corr * matrix; scale = new Vector3(extractedScale.m00, extractedScale.m11, extractedScale.m22); } // convert transform values from left handed to right handed return Matrix4x4.TRS(position, rotation, scale); } } }